Fix code reading from the pipe stream in exec sample.
[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
417 frame->Show(true);
418
419 // success: wxApp::OnRun() will be called which will enter the main message
420 // loop and the application will run. If we returned false here, the
421 // application would exit immediately.
422 return true;
423 }
424
425 // ----------------------------------------------------------------------------
426 // main frame
427 // ----------------------------------------------------------------------------
428
429 #ifdef __VISUALC__
430 #pragma warning(disable: 4355) // this used in base member initializer list
431 #endif
432
433 // frame constructor
434 MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
435 : wxFrame((wxFrame *)NULL, wxID_ANY, title, pos, size),
436 m_timerIdleWakeUp(this, Exec_TimerIdle),
437 m_timerBg(this, Exec_TimerBg)
438 {
439 m_pidLast = 0;
440
441 #ifdef __WXMAC__
442 // we need this in order to allow the about menu relocation, since ABOUT is
443 // not the default id of the about menu
444 wxApp::s_macAboutMenuItemId = Exec_About;
445 #endif
446
447 // create a menu bar
448 wxMenu *menuFile = new wxMenu(wxEmptyString, wxMENU_TEAROFF);
449 menuFile->Append(Exec_Kill, wxT("&Kill process...\tCtrl-K"),
450 wxT("Kill a process by PID"));
451 menuFile->AppendSeparator();
452 menuFile->Append(Exec_ClearLog, wxT("&Clear log\tCtrl-L"),
453 wxT("Clear the log window"));
454 menuFile->AppendSeparator();
455 menuFile->Append(Exec_BeginBusyCursor, wxT("Show &busy cursor\tCtrl-C"));
456 menuFile->Append(Exec_EndBusyCursor, wxT("Show &normal cursor\tShift-Ctrl-C"));
457 menuFile->AppendSeparator();
458 menuFile->Append(Exec_Quit, wxT("E&xit\tAlt-X"), wxT("Quit this program"));
459
460 wxMenu *execMenu = new wxMenu;
461 execMenu->Append(Exec_SyncExec, wxT("Sync &execution...\tCtrl-E"),
462 wxT("Launch a program and return when it terminates"));
463 execMenu->Append(Exec_SyncNoEventsExec, wxT("Sync execution and &block...\tCtrl-B"),
464 wxT("Launch a program and block until it terminates"));
465 execMenu->Append(Exec_AsyncExec, wxT("&Async execution...\tCtrl-A"),
466 wxT("Launch a program and return immediately"));
467 execMenu->Append(Exec_Shell, wxT("Execute &shell command...\tCtrl-S"),
468 wxT("Launch a shell and execute a command in it"));
469 execMenu->AppendSeparator();
470 execMenu->Append(Exec_Redirect, wxT("Capture command &output...\tCtrl-O"),
471 wxT("Launch a program and capture its output"));
472 execMenu->Append(Exec_Pipe, wxT("&Pipe through command..."),
473 wxT("Pipe a string through a filter"));
474 execMenu->Append(Exec_POpen, wxT("&Open a pipe to a command...\tCtrl-P"),
475 wxT("Open a pipe to and from another program"));
476
477 execMenu->AppendSeparator();
478 execMenu->Append(Exec_OpenFile, wxT("Open &file...\tCtrl-F"),
479 wxT("Launch the command to open this kind of files"));
480 execMenu->Append(Exec_LaunchFile, wxT("La&unch file...\tShift-Ctrl-F"),
481 wxT("Launch the default application associated with the file"));
482 execMenu->Append(Exec_OpenURL, wxT("Open &URL...\tCtrl-U"),
483 wxT("Launch the default browser with the given URL"));
484 #ifdef __WINDOWS__
485 execMenu->AppendSeparator();
486 execMenu->Append(Exec_DDEExec, wxT("Execute command via &DDE...\tCtrl-D"));
487 execMenu->Append(Exec_DDERequest, wxT("Send DDE &request...\tCtrl-R"));
488 #endif
489
490 wxMenu *helpMenu = new wxMenu(wxEmptyString, wxMENU_TEAROFF);
491 helpMenu->Append(Exec_About, wxT("&About...\tF1"), wxT("Show about dialog"));
492
493 // now append the freshly created menu to the menu bar...
494 wxMenuBar *menuBar = new wxMenuBar();
495 menuBar->Append(menuFile, wxT("&File"));
496 menuBar->Append(execMenu, wxT("&Exec"));
497 menuBar->Append(helpMenu, wxT("&Help"));
498
499 // ... and attach this menu bar to the frame
500 SetMenuBar(menuBar);
501
502 // create the listbox in which we will show misc messages as they come
503 m_lbox = new wxListBox(this, wxID_ANY);
504 wxFont font(12, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL,
505 wxFONTWEIGHT_NORMAL);
506 if ( font.IsOk() )
507 m_lbox->SetFont(font);
508
509 #if wxUSE_STATUSBAR
510 // create a status bar just for fun (by default with 1 pane only)
511 CreateStatusBar(2);
512 SetStatusText(wxT("Welcome to wxWidgets exec sample!"));
513 #endif // wxUSE_STATUSBAR
514
515 m_timerBg.Start(1000);
516 }
517
518 MyFrame::~MyFrame()
519 {
520 // any processes left until now must be deleted manually: normally this is
521 // done when the associated process terminates but it must be still running
522 // if this didn't happen until now
523 for ( size_t n = 0; n < m_allAsync.size(); n++ )
524 {
525 delete m_allAsync[n];
526 }
527 }
528
529 // ----------------------------------------------------------------------------
530 // event handlers: file and help menu
531 // ----------------------------------------------------------------------------
532
533 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
534 {
535 // true is to force the frame to close
536 Close(true);
537 }
538
539 void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
540 {
541 m_lbox->Clear();
542 }
543
544 void MyFrame::OnBeginBusyCursor(wxCommandEvent& WXUNUSED(event))
545 {
546 wxBeginBusyCursor();
547 }
548
549 void MyFrame::OnEndBusyCursor(wxCommandEvent& WXUNUSED(event))
550 {
551 wxEndBusyCursor();
552 }
553
554 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
555 {
556 wxMessageBox(wxT("Exec wxWidgets Sample\n(c) 2000-2002 Vadim Zeitlin"),
557 wxT("About Exec"), wxOK | wxICON_INFORMATION, this);
558 }
559
560 void MyFrame::OnKill(wxCommandEvent& WXUNUSED(event))
561 {
562 long pid = wxGetNumberFromUser(wxT("Please specify the process to kill"),
563 wxT("Enter PID:"),
564 wxT("Exec question"),
565 m_pidLast,
566 // we need the full unsigned int range
567 -INT_MAX, INT_MAX,
568 this);
569 if ( pid == -1 )
570 {
571 // cancelled
572 return;
573 }
574
575 m_pidLast = pid;
576
577 static const wxString signalNames[] =
578 {
579 wxT("Just test (SIGNONE)"),
580 wxT("Hangup (SIGHUP)"),
581 wxT("Interrupt (SIGINT)"),
582 wxT("Quit (SIGQUIT)"),
583 wxT("Illegal instruction (SIGILL)"),
584 wxT("Trap (SIGTRAP)"),
585 wxT("Abort (SIGABRT)"),
586 wxT("Emulated trap (SIGEMT)"),
587 wxT("FP exception (SIGFPE)"),
588 wxT("Kill (SIGKILL)"),
589 wxT("Bus (SIGBUS)"),
590 wxT("Segment violation (SIGSEGV)"),
591 wxT("System (SIGSYS)"),
592 wxT("Broken pipe (SIGPIPE)"),
593 wxT("Alarm (SIGALRM)"),
594 wxT("Terminate (SIGTERM)"),
595 };
596
597 static int s_sigLast = wxSIGNONE;
598 int sig = wxGetSingleChoiceIndex(wxT("How to kill the process?"),
599 wxT("Exec question"),
600 WXSIZEOF(signalNames), signalNames,
601 s_sigLast,
602 this);
603 switch ( sig )
604 {
605 default:
606 wxFAIL_MSG( wxT("unexpected return value") );
607 // fall through
608
609 case -1:
610 // cancelled
611 return;
612
613 case wxSIGNONE:
614 case wxSIGHUP:
615 case wxSIGINT:
616 case wxSIGQUIT:
617 case wxSIGILL:
618 case wxSIGTRAP:
619 case wxSIGABRT:
620 case wxSIGEMT:
621 case wxSIGFPE:
622 case wxSIGKILL:
623 case wxSIGBUS:
624 case wxSIGSEGV:
625 case wxSIGSYS:
626 case wxSIGPIPE:
627 case wxSIGALRM:
628 case wxSIGTERM:
629 break;
630 }
631
632 s_sigLast = sig;
633
634 if ( sig == wxSIGNONE )
635 {
636 // This simply calls Kill(wxSIGNONE) but using it is more convenient.
637 if ( wxProcess::Exists(pid) )
638 {
639 wxLogStatus(wxT("Process %ld is running."), pid);
640 }
641 else
642 {
643 wxLogStatus(wxT("No process with pid = %ld."), pid);
644 }
645 }
646 else // not SIGNONE
647 {
648 wxKillError rc = wxProcess::Kill(pid, (wxSignal)sig);
649 if ( rc == wxKILL_OK )
650 {
651 wxLogStatus(wxT("Process %ld killed with signal %d."), pid, sig);
652 }
653 else
654 {
655 static const wxChar *errorText[] =
656 {
657 wxT(""), // no error
658 wxT("signal not supported"),
659 wxT("permission denied"),
660 wxT("no such process"),
661 wxT("unspecified error"),
662 };
663
664 wxLogStatus(wxT("Failed to kill process %ld with signal %d: %s"),
665 pid, sig, errorText[rc]);
666 }
667 }
668 }
669
670 // ----------------------------------------------------------------------------
671 // execution options dialog
672 // ----------------------------------------------------------------------------
673
674 enum ExecQueryDialogID
675 {
676 TEXT_EXECUTABLE,
677 TEXT_CWD,
678 TEXT_ENVIRONMENT
679 };
680
681 class ExecQueryDialog : public wxDialog
682 {
683 public:
684 ExecQueryDialog(const wxString& cmd);
685
686 wxString GetExecutable() const
687 {
688 return m_executable->GetValue();
689 }
690
691 wxString GetWorkDir() const
692 {
693 return m_useCWD->GetValue() ? m_cwdtext->GetValue() : wxString();
694 }
695
696 void GetEnvironment(wxEnvVariableHashMap& env);
697
698 private:
699 void OnUpdateWorkingDirectoryUI(wxUpdateUIEvent& event)
700 {
701 event.Enable(m_useCWD->GetValue());
702 }
703
704 void OnUpdateEnvironmentUI(wxUpdateUIEvent& event)
705 {
706 event.Enable(m_useEnv->GetValue());
707 }
708
709 wxTextCtrl* m_executable;
710 wxTextCtrl* m_cwdtext;
711 wxTextCtrl* m_envtext;
712 wxCheckBox* m_useCWD;
713 wxCheckBox* m_useEnv;
714
715 DECLARE_EVENT_TABLE()
716 };
717
718 BEGIN_EVENT_TABLE(ExecQueryDialog, wxDialog)
719 EVT_UPDATE_UI(TEXT_CWD, ExecQueryDialog::OnUpdateWorkingDirectoryUI)
720 EVT_UPDATE_UI(TEXT_ENVIRONMENT, ExecQueryDialog::OnUpdateEnvironmentUI)
721 END_EVENT_TABLE()
722
723 ExecQueryDialog::ExecQueryDialog(const wxString& cmd)
724 : wxDialog(NULL, wxID_ANY, DIALOG_TITLE,
725 wxDefaultPosition, wxDefaultSize,
726 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
727 {
728 wxSizer* globalSizer = new wxBoxSizer(wxVERTICAL);
729
730 m_executable = new wxTextCtrl(this, TEXT_EXECUTABLE, wxString());
731 m_cwdtext = new wxTextCtrl(this, TEXT_CWD, wxString());
732 m_envtext = new wxTextCtrl(this, TEXT_ENVIRONMENT, wxString(),
733 wxDefaultPosition, wxSize(300, 200),
734 wxTE_MULTILINE|wxHSCROLL);
735
736 const wxSizerFlags flagsExpand = wxSizerFlags().Expand().Border();
737 globalSizer->Add(new wxStaticText(this, wxID_ANY, "Enter the command: "),
738 flagsExpand);
739 globalSizer->Add(m_executable, flagsExpand);
740
741 m_useCWD = new wxCheckBox(this, wxID_ANY, "Working directory: ");
742 globalSizer->Add(m_useCWD, flagsExpand);
743 globalSizer->Add(m_cwdtext, flagsExpand);
744
745 m_useEnv = new wxCheckBox(this, wxID_ANY, "Environment: ");
746 globalSizer->Add(m_useEnv, flagsExpand);
747 globalSizer->Add(m_envtext, wxSizerFlags(flagsExpand).Proportion(1));
748
749 globalSizer->Add(CreateStdDialogButtonSizer(wxOK|wxCANCEL), flagsExpand);
750 SetSizerAndFit(globalSizer);
751
752
753 m_executable->SetValue(cmd);
754 m_cwdtext->SetValue(wxGetCwd());
755 wxEnvVariableHashMap env;
756 if ( wxGetEnvMap(&env) )
757 {
758 for ( wxEnvVariableHashMap::iterator it = env.begin();
759 it != env.end();
760 ++it )
761 {
762 m_envtext->AppendText(it->first + '=' + it->second + '\n');
763 }
764 }
765 m_useCWD->SetValue(false);
766 m_useEnv->SetValue(false);
767 }
768
769 void ExecQueryDialog::GetEnvironment(wxEnvVariableHashMap& env)
770 {
771 env.clear();
772 if ( m_useEnv->GetValue() )
773 {
774 wxString name,
775 value;
776
777 const int nb = m_envtext->GetNumberOfLines();
778 for ( int l = 0; l < nb; l++ )
779 {
780 const wxString line = m_envtext->GetLineText(l).Trim();
781
782 if ( !line.empty() )
783 {
784 name = line.BeforeFirst('=', &value);
785 if ( name.empty() )
786 {
787 wxLogWarning("Skipping invalid environment line \"%s\".", line);
788 continue;
789 }
790
791 env[name] = value;
792 }
793 }
794 }
795 }
796
797 static bool QueryExec(wxString& cmd, wxExecuteEnv& env)
798 {
799 ExecQueryDialog dialog(cmd);
800
801 if ( dialog.ShowModal() != wxID_OK )
802 return false;
803
804 cmd = dialog.GetExecutable();
805 env.cwd = dialog.GetWorkDir();
806 dialog.GetEnvironment(env.env);
807
808 return true;
809 }
810
811 // ----------------------------------------------------------------------------
812 // event handlers: exec menu
813 // ----------------------------------------------------------------------------
814
815 void MyFrame::DoAsyncExec(const wxString& cmd)
816 {
817 MyProcess * const process = new MyProcess(this, cmd);
818 m_pidLast = wxExecute(cmd, wxEXEC_ASYNC, process);
819 if ( !m_pidLast )
820 {
821 wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
822
823 delete process;
824 }
825 else
826 {
827 wxLogStatus(wxT("Process %ld (%s) launched."), m_pidLast, cmd.c_str());
828
829 m_cmdLast = cmd;
830
831 // the parent frame keeps track of all async processes as it needs to
832 // free them if we exit before the child process terminates
833 AddAsyncProcess(process);
834 }
835 }
836
837 void MyFrame::OnSyncExec(wxCommandEvent& WXUNUSED(event))
838 {
839 wxString cmd;
840 wxExecuteEnv env;
841 if ( !QueryExec(cmd, env) )
842 return;
843
844 wxLogStatus( wxT("'%s' is running please wait..."), cmd.c_str() );
845
846 int code = wxExecute(cmd, wxEXEC_SYNC, NULL, &env);
847
848 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
849 cmd.c_str(), code);
850
851 m_cmdLast = cmd;
852 }
853
854 void MyFrame::OnSyncNoEventsExec(wxCommandEvent& WXUNUSED(event))
855 {
856 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
857 DIALOG_TITLE,
858 m_cmdLast);
859
860 if ( !cmd )
861 return;
862
863 wxLogStatus( wxT("'%s' is running please wait..."), cmd.c_str() );
864
865 int code = wxExecute(cmd, wxEXEC_BLOCK);
866
867 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
868 cmd.c_str(), code);
869
870 m_cmdLast = cmd;
871 }
872
873 void MyFrame::OnAsyncExec(wxCommandEvent& WXUNUSED(event))
874 {
875 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
876 DIALOG_TITLE,
877 m_cmdLast);
878
879 if ( !cmd )
880 return;
881
882 DoAsyncExec(cmd);
883 }
884
885 void MyFrame::OnShell(wxCommandEvent& WXUNUSED(event))
886 {
887 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
888 DIALOG_TITLE,
889 m_cmdLast);
890
891 if ( !cmd )
892 return;
893
894 int code = wxShell(cmd);
895 wxLogStatus(wxT("Shell command '%s' terminated with exit code %d."),
896 cmd.c_str(), code);
897 m_cmdLast = cmd;
898 }
899
900 void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event))
901 {
902 if ( !m_cmdLast )
903 {
904 #ifdef __WXMSW__
905 m_cmdLast = "type Makefile.in";
906 #else
907 m_cmdLast = "cat -n Makefile";
908 #endif
909 }
910
911 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
912 DIALOG_TITLE,
913 m_cmdLast);
914
915 if ( !cmd )
916 return;
917
918 bool sync;
919 switch ( wxMessageBox(wxT("Execute it synchronously?"),
920 wxT("Exec question"),
921 wxYES_NO | wxCANCEL | wxICON_QUESTION, this) )
922 {
923 case wxYES:
924 sync = true;
925 break;
926
927 case wxNO:
928 sync = false;
929 break;
930
931 default:
932 return;
933 }
934
935 if ( sync )
936 {
937 wxLogStatus("\"%s\" is running please wait...", cmd);
938
939 wxStopWatch sw;
940
941 wxArrayString output, errors;
942 int code = wxExecute(cmd, output, errors);
943
944 wxLogStatus("Command \"%s\" terminated after %ldms; exit code %d.",
945 cmd, sw.Time(), code);
946
947 if ( code != -1 )
948 {
949 ShowOutput(cmd, output, wxT("Output"));
950 ShowOutput(cmd, errors, wxT("Errors"));
951 }
952 }
953 else // async exec
954 {
955 MyPipedProcess *process = new MyPipedProcess(this, cmd);
956 if ( !wxExecute(cmd, wxEXEC_ASYNC, process) )
957 {
958 wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
959
960 delete process;
961 }
962 else
963 {
964 AddPipedProcess(process);
965 }
966 }
967
968 m_cmdLast = cmd;
969 }
970
971 void MyFrame::OnExecWithPipe(wxCommandEvent& WXUNUSED(event))
972 {
973 if ( !m_cmdLast )
974 m_cmdLast = wxT("tr [a-z] [A-Z]");
975
976 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
977 DIALOG_TITLE,
978 m_cmdLast);
979
980 if ( !cmd )
981 return;
982
983 wxString input = wxGetTextFromUser(wxT("Enter the string to send to it: "),
984 DIALOG_TITLE);
985 if ( !input )
986 return;
987
988 // always execute the filter asynchronously
989 MyPipedProcess2 *process = new MyPipedProcess2(this, cmd, input);
990 long pid = wxExecute(cmd, wxEXEC_ASYNC, process);
991 if ( pid )
992 {
993 wxLogStatus(wxT("Process %ld (%s) launched."), pid, cmd.c_str());
994
995 AddPipedProcess(process);
996 }
997 else
998 {
999 wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
1000
1001 delete process;
1002 }
1003
1004 m_cmdLast = cmd;
1005 }
1006
1007 void MyFrame::OnPOpen(wxCommandEvent& WXUNUSED(event))
1008 {
1009 wxString cmd = wxGetTextFromUser(wxT("Enter the command to launch: "),
1010 DIALOG_TITLE,
1011 m_cmdLast);
1012 if ( cmd.empty() )
1013 return;
1014
1015 wxProcess *process = wxProcess::Open(cmd);
1016 if ( !process )
1017 {
1018 wxLogError(wxT("Failed to launch the command."));
1019 return;
1020 }
1021
1022 wxLogVerbose(wxT("PID of the new process: %ld"), process->GetPid());
1023
1024 wxOutputStream *out = process->GetOutputStream();
1025 if ( !out )
1026 {
1027 wxLogError(wxT("Failed to connect to child stdin"));
1028 return;
1029 }
1030
1031 wxInputStream *in = process->GetInputStream();
1032 if ( !in )
1033 {
1034 wxLogError(wxT("Failed to connect to child stdout"));
1035 return;
1036 }
1037
1038 new MyPipeFrame(this, cmd, process);
1039 }
1040
1041 static wxString gs_lastFile;
1042
1043 static bool AskUserForFileName()
1044 {
1045 wxString filename;
1046
1047 #if wxUSE_FILEDLG
1048 filename = wxLoadFileSelector(wxT("any"), wxEmptyString, gs_lastFile);
1049 #else // !wxUSE_FILEDLG
1050 filename = wxGetTextFromUser(wxT("Enter the file name"), wxT("exec sample"),
1051 gs_lastFile);
1052 #endif // wxUSE_FILEDLG/!wxUSE_FILEDLG
1053
1054 if ( filename.empty() )
1055 return false;
1056
1057 gs_lastFile = filename;
1058
1059 return true;
1060 }
1061
1062 void MyFrame::OnFileExec(wxCommandEvent& WXUNUSED(event))
1063 {
1064 if ( !AskUserForFileName() )
1065 return;
1066
1067 wxString ext = gs_lastFile.AfterLast(wxT('.'));
1068 wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(ext);
1069 if ( !ft )
1070 {
1071 wxLogError(wxT("Impossible to determine the file type for extension '%s'"),
1072 ext.c_str());
1073 return;
1074 }
1075
1076 wxString cmd;
1077 bool ok = ft->GetOpenCommand(&cmd,
1078 wxFileType::MessageParameters(gs_lastFile));
1079 delete ft;
1080 if ( !ok )
1081 {
1082 wxLogError(wxT("Impossible to find out how to open files of extension '%s'"),
1083 ext.c_str());
1084 return;
1085 }
1086
1087 DoAsyncExec(cmd);
1088 }
1089
1090 void MyFrame::OnFileLaunch(wxCommandEvent& WXUNUSED(event))
1091 {
1092 if ( !AskUserForFileName() )
1093 return;
1094
1095 if ( !wxLaunchDefaultApplication(gs_lastFile) )
1096 {
1097 wxLogError("Opening \"%s\" in default application failed.", gs_lastFile);
1098 }
1099 }
1100
1101 void MyFrame::OnOpenURL(wxCommandEvent& WXUNUSED(event))
1102 {
1103 static wxString s_url(wxT("http://www.wxwidgets.org/"));
1104
1105 wxString filename = wxGetTextFromUser
1106 (
1107 wxT("Enter the URL"),
1108 wxT("exec sample"),
1109 s_url,
1110 this
1111 );
1112
1113 if ( filename.empty() )
1114 return;
1115
1116 s_url = filename;
1117
1118 if ( !wxLaunchDefaultBrowser(s_url) )
1119 {
1120 wxLogError(wxT("Failed to open URL \"%s\""), s_url.c_str());
1121 }
1122 }
1123
1124 // ----------------------------------------------------------------------------
1125 // DDE stuff
1126 // ----------------------------------------------------------------------------
1127
1128 #ifdef __WINDOWS__
1129
1130 bool MyFrame::GetDDEServer()
1131 {
1132 wxString server = wxGetTextFromUser(wxT("Server to connect to:"),
1133 DIALOG_TITLE, m_server);
1134 if ( !server )
1135 return false;
1136
1137 m_server = server;
1138
1139 wxString topic = wxGetTextFromUser(wxT("DDE topic:"), DIALOG_TITLE, m_topic);
1140 if ( !topic )
1141 return false;
1142
1143 m_topic = topic;
1144
1145 wxString cmd = wxGetTextFromUser(wxT("DDE command:"), DIALOG_TITLE, m_cmdDde);
1146 if ( !cmd )
1147 return false;
1148
1149 m_cmdDde = cmd;
1150
1151 return true;
1152 }
1153
1154 void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event))
1155 {
1156 if ( !GetDDEServer() )
1157 return;
1158
1159 wxDDEClient client;
1160 wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic);
1161 if ( !conn )
1162 {
1163 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1164 m_server.c_str());
1165 }
1166 else
1167 {
1168 if ( !conn->Execute(m_cmdDde) )
1169 {
1170 wxLogError(wxT("Failed to execute command '%s' via DDE."),
1171 m_cmdDde.c_str());
1172 }
1173 else
1174 {
1175 wxLogStatus(wxT("Successfully executed DDE command"));
1176 }
1177 }
1178 }
1179
1180 void MyFrame::OnDDERequest(wxCommandEvent& WXUNUSED(event))
1181 {
1182 if ( !GetDDEServer() )
1183 return;
1184
1185 wxDDEClient client;
1186 wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic);
1187 if ( !conn )
1188 {
1189 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1190 m_server.c_str());
1191 }
1192 else
1193 {
1194 if ( !conn->Request(m_cmdDde) )
1195 {
1196 wxLogError(wxT("Failed to send request '%s' via DDE."),
1197 m_cmdDde.c_str());
1198 }
1199 else
1200 {
1201 wxLogStatus(wxT("Successfully sent DDE request."));
1202 }
1203 }
1204 }
1205
1206 #endif // __WINDOWS__
1207
1208 // ----------------------------------------------------------------------------
1209 // various helpers
1210 // ----------------------------------------------------------------------------
1211
1212 // input polling
1213 void MyFrame::OnIdle(wxIdleEvent& event)
1214 {
1215 size_t count = m_running.GetCount();
1216 for ( size_t n = 0; n < count; n++ )
1217 {
1218 if ( m_running[n]->HasInput() )
1219 {
1220 event.RequestMore();
1221 }
1222 }
1223 }
1224
1225 void MyFrame::OnIdleTimer(wxTimerEvent& WXUNUSED(event))
1226 {
1227 wxWakeUpIdle();
1228 }
1229
1230 void MyFrame::OnBgTimer(wxTimerEvent& WXUNUSED(event))
1231 {
1232 static unsigned long s_ticks = 0;
1233 SetStatusText(wxString::Format("%lu ticks", s_ticks++), 1);
1234 }
1235
1236 void MyFrame::OnProcessTerminated(MyPipedProcess *process)
1237 {
1238 RemovePipedProcess(process);
1239 }
1240
1241 void MyFrame::OnAsyncTermination(MyProcess *process)
1242 {
1243 m_allAsync.Remove(process);
1244
1245 delete process;
1246 }
1247
1248 void MyFrame::AddPipedProcess(MyPipedProcess *process)
1249 {
1250 if ( m_running.IsEmpty() )
1251 {
1252 // we want to start getting the timer events to ensure that a
1253 // steady stream of idle events comes in -- otherwise we
1254 // wouldn't be able to poll the child process input
1255 m_timerIdleWakeUp.Start(100);
1256 }
1257 //else: the timer is already running
1258
1259 m_running.Add(process);
1260 m_allAsync.Add(process);
1261 }
1262
1263 void MyFrame::RemovePipedProcess(MyPipedProcess *process)
1264 {
1265 m_running.Remove(process);
1266
1267 if ( m_running.IsEmpty() )
1268 {
1269 // we don't need to get idle events all the time any more
1270 m_timerIdleWakeUp.Stop();
1271 }
1272 }
1273
1274 void MyFrame::ShowOutput(const wxString& cmd,
1275 const wxArrayString& output,
1276 const wxString& title)
1277 {
1278 size_t count = output.GetCount();
1279 if ( !count )
1280 return;
1281
1282 m_lbox->Append(wxString::Format(wxT("--- %s of '%s' ---"),
1283 title.c_str(), cmd.c_str()));
1284
1285 for ( size_t n = 0; n < count; n++ )
1286 {
1287 m_lbox->Append(output[n]);
1288 }
1289
1290 m_lbox->Append(wxString::Format(wxT("--- End of %s ---"),
1291 title.Lower().c_str()));
1292 }
1293
1294 // ----------------------------------------------------------------------------
1295 // MyProcess
1296 // ----------------------------------------------------------------------------
1297
1298 void MyProcess::OnTerminate(int pid, int status)
1299 {
1300 wxLogStatus(m_parent, wxT("Process %u ('%s') terminated with exit code %d."),
1301 pid, m_cmd.c_str(), status);
1302
1303 m_parent->OnAsyncTermination(this);
1304 }
1305
1306 // ----------------------------------------------------------------------------
1307 // MyPipedProcess
1308 // ----------------------------------------------------------------------------
1309
1310 bool MyPipedProcess::HasInput()
1311 {
1312 bool hasInput = false;
1313
1314 if ( IsInputAvailable() )
1315 {
1316 wxTextInputStream tis(*GetInputStream());
1317
1318 // this assumes that the output is always line buffered
1319 wxString msg;
1320 msg << m_cmd << wxT(" (stdout): ") << tis.ReadLine();
1321
1322 m_parent->GetLogListBox()->Append(msg);
1323
1324 hasInput = true;
1325 }
1326
1327 if ( IsErrorAvailable() )
1328 {
1329 wxTextInputStream tis(*GetErrorStream());
1330
1331 // this assumes that the output is always line buffered
1332 wxString msg;
1333 msg << m_cmd << wxT(" (stderr): ") << tis.ReadLine();
1334
1335 m_parent->GetLogListBox()->Append(msg);
1336
1337 hasInput = true;
1338 }
1339
1340 return hasInput;
1341 }
1342
1343 void MyPipedProcess::OnTerminate(int pid, int status)
1344 {
1345 // show the rest of the output
1346 while ( HasInput() )
1347 ;
1348
1349 m_parent->OnProcessTerminated(this);
1350
1351 MyProcess::OnTerminate(pid, status);
1352 }
1353
1354 // ----------------------------------------------------------------------------
1355 // MyPipedProcess2
1356 // ----------------------------------------------------------------------------
1357
1358 bool MyPipedProcess2::HasInput()
1359 {
1360 if ( !m_input.empty() )
1361 {
1362 wxTextOutputStream os(*GetOutputStream());
1363 os.WriteString(m_input);
1364
1365 CloseOutput();
1366 m_input.clear();
1367
1368 // call us once again - may be we'll have output
1369 return true;
1370 }
1371
1372 return MyPipedProcess::HasInput();
1373 }
1374
1375 // ============================================================================
1376 // MyPipeFrame implementation
1377 // ============================================================================
1378
1379 MyPipeFrame::MyPipeFrame(wxFrame *parent,
1380 const wxString& cmd,
1381 wxProcess *process)
1382 : wxFrame(parent, wxID_ANY, cmd),
1383 m_process(process),
1384 // in a real program we'd check that the streams are !NULL here
1385 m_out(*process->GetOutputStream()),
1386 m_in(*process->GetInputStream()),
1387 m_err(*process->GetErrorStream())
1388 {
1389 m_process->SetNextHandler(this);
1390
1391 wxPanel *panel = new wxPanel(this, wxID_ANY);
1392
1393 m_textOut = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1394 wxDefaultPosition, wxDefaultSize,
1395 wxTE_PROCESS_ENTER);
1396 m_textIn = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1397 wxDefaultPosition, wxDefaultSize,
1398 wxTE_MULTILINE | wxTE_RICH);
1399 m_textIn->SetEditable(false);
1400 m_textErr = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1401 wxDefaultPosition, wxDefaultSize,
1402 wxTE_MULTILINE | wxTE_RICH);
1403 m_textErr->SetEditable(false);
1404
1405 wxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
1406 sizerTop->Add(m_textOut, 0, wxGROW | wxALL, 5);
1407
1408 wxSizer *sizerBtns = new wxBoxSizer(wxHORIZONTAL);
1409 sizerBtns->
1410 Add(new wxButton(panel, Exec_Btn_Send, wxT("&Send")), 0, wxALL, 5);
1411 sizerBtns->
1412 Add(new wxButton(panel, Exec_Btn_SendFile, wxT("&File...")), 0, wxALL, 5);
1413 sizerBtns->
1414 Add(new wxButton(panel, Exec_Btn_Get, wxT("&Get")), 0, wxALL, 5);
1415 sizerBtns->
1416 Add(new wxButton(panel, Exec_Btn_Close, wxT("&Close")), 0, wxALL, 5);
1417
1418 sizerTop->Add(sizerBtns, 0, wxCENTRE | wxALL, 5);
1419 sizerTop->Add(m_textIn, 1, wxGROW | wxALL, 5);
1420 sizerTop->Add(m_textErr, 1, wxGROW | wxALL, 5);
1421
1422 panel->SetSizer(sizerTop);
1423 sizerTop->Fit(this);
1424
1425 Show();
1426 }
1427
1428 void MyPipeFrame::OnBtnSendFile(wxCommandEvent& WXUNUSED(event))
1429 {
1430 #if wxUSE_FILEDLG
1431 wxFileDialog filedlg(this, wxT("Select file to send"));
1432 if ( filedlg.ShowModal() != wxID_OK )
1433 return;
1434
1435 wxFFile file(filedlg.GetFilename(), wxT("r"));
1436 wxString data;
1437 if ( !file.IsOpened() || !file.ReadAll(&data) )
1438 return;
1439
1440 // can't write the entire string at once, this risk overflowing the pipe
1441 // and we would dead lock
1442 size_t len = data.length();
1443 const wxChar *pc = data.c_str();
1444 while ( len )
1445 {
1446 const size_t CHUNK_SIZE = 4096;
1447 m_out.Write(pc, len > CHUNK_SIZE ? CHUNK_SIZE : len);
1448
1449 // note that not all data could have been written as we don't block on
1450 // the write end of the pipe
1451 const size_t lenChunk = m_out.LastWrite();
1452
1453 pc += lenChunk;
1454 len -= lenChunk;
1455
1456 DoGet();
1457 }
1458 #endif // wxUSE_FILEDLG
1459 }
1460
1461 void MyPipeFrame::DoGet()
1462 {
1463 // we don't have any way to be notified when any input appears on the
1464 // stream so we have to poll it :-(
1465 DoGetFromStream(m_textIn, m_in);
1466 DoGetFromStream(m_textErr, m_err);
1467 }
1468
1469 void MyPipeFrame::DoGetFromStream(wxTextCtrl *text, wxInputStream& in)
1470 {
1471 while ( in.CanRead() )
1472 {
1473 char buffer[4096];
1474 buffer[in.Read(buffer, WXSIZEOF(buffer) - 1).LastRead()] = '\0';
1475
1476 text->AppendText(buffer);
1477 }
1478 }
1479
1480 void MyPipeFrame::DoClose()
1481 {
1482 m_process->CloseOutput();
1483
1484 DisableInput();
1485 }
1486
1487 void MyPipeFrame::DisableInput()
1488 {
1489 m_textOut->SetEditable(false);
1490 FindWindow(Exec_Btn_Send)->Disable();
1491 FindWindow(Exec_Btn_SendFile)->Disable();
1492 FindWindow(Exec_Btn_Close)->Disable();
1493 }
1494
1495 void MyPipeFrame::DisableOutput()
1496 {
1497 FindWindow(Exec_Btn_Get)->Disable();
1498 }
1499
1500 void MyPipeFrame::OnClose(wxCloseEvent& event)
1501 {
1502 if ( m_process )
1503 {
1504 // we're not interested in getting the process termination notification
1505 // if we are closing it ourselves
1506 wxProcess *process = m_process;
1507 m_process = NULL;
1508 process->SetNextHandler(NULL);
1509
1510 process->CloseOutput();
1511 }
1512
1513 event.Skip();
1514 }
1515
1516 void MyPipeFrame::OnProcessTerm(wxProcessEvent& WXUNUSED(event))
1517 {
1518 DoGet();
1519
1520 wxDELETE(m_process);
1521
1522 wxLogWarning(wxT("The other process has terminated, closing"));
1523
1524 DisableInput();
1525 DisableOutput();
1526 }