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