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