a bit of cleanup
[wxWidgets.git] / src / cocoa / utilsexc.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        utilsexec.mm
3 // Purpose:     Execution-related utilities for wxCocoa
4 // Author:      Ryan Norton (carbon darwin version based off of Stefan's code)
5 // Modified by:
6 // Created:     2004-10-05
7 // RCS-ID:      $Id$
8 // Copyright:   (c) Ryan Norton
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 //#pragma implementation
14 #endif
15
16 #include "wx/log.h"
17 #include "wx/utils.h"
18 #ifdef __DARWIN__
19 #include "wx/unix/execute.h"
20 #include <unistd.h>
21 #include <sys/wait.h>
22 extern "C" {
23 #include <mach/mach.h>
24 }
25 #include <CoreFoundation/CFMachPort.h>
26 #endif
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #ifndef __DARWIN__
33
34 long wxExecute(const wxString& command, int flags, wxProcess *WXUNUSED(handler))
35 {
36     wxFAIL_MSG( _T("wxExecute() not yet implemented") );
37     return 0;
38 }
39
40 #endif
41
42 #ifdef __DARWIN__
43 void wxMAC_MachPortEndProcessDetect(CFMachPortRef port, void *data)
44 {
45         wxEndProcessData *proc_data = (wxEndProcessData*)data;
46         wxLogDebug(wxT("Wow.. this actually worked!"));
47         int status = 0;
48         int rc = waitpid(abs(proc_data->pid), &status, WNOHANG);
49         if(!rc)
50         {
51                 wxLogDebug(wxT("Mach port was invalidated, but process hasn't terminated!"));
52                 return;
53         }
54         if((rc != -1) && WIFEXITED(status))
55                 proc_data->exitcode = WEXITSTATUS(status);
56         else
57                 proc_data->exitcode = -1;
58         wxHandleProcessTermination(proc_data);
59 }
60
61 int wxAddProcessCallbackForPid(wxEndProcessData *proc_data, int pid)
62 {
63     if(pid < 1)
64         return -1;
65     kern_return_t    kernResult;
66     mach_port_t    taskOfOurProcess;
67     mach_port_t    machPortForProcess;
68     taskOfOurProcess = mach_task_self();
69     if(taskOfOurProcess == MACH_PORT_NULL)
70     {
71         wxLogDebug(wxT("No mach_task_self()"));
72         return -1;
73     }
74     wxLogDebug(wxT("pid=%d"),pid);
75     kernResult = task_for_pid(taskOfOurProcess,pid, &machPortForProcess);
76     if(kernResult != KERN_SUCCESS)
77     {
78         wxLogDebug(wxT("no task_for_pid()"));
79         // try seeing if it is already dead or something
80         // FIXME: a better method would be to call the callback function
81         // from idle time until the process terminates. Of course, how
82         // likely is it that it will take more than 0.1 seconds for the
83         // mach terminate event to make its way to the BSD subsystem?
84         usleep(100); // sleep for 0.1 seconds
85         wxMAC_MachPortEndProcessDetect(NULL, (void*)proc_data);
86         return -1;
87     }
88     CFMachPortContext termcb_contextinfo;
89     termcb_contextinfo.version = NULL;
90     termcb_contextinfo.info = (void*)proc_data;
91     termcb_contextinfo.retain = NULL;
92     termcb_contextinfo.release = NULL;
93     termcb_contextinfo.copyDescription = NULL;
94     CFMachPortRef    CFMachPortForProcess;
95     Boolean        ShouldFreePort;
96     CFMachPortForProcess = CFMachPortCreateWithPort(NULL, machPortForProcess, NULL, &termcb_contextinfo, &ShouldFreePort);
97     if(!CFMachPortForProcess)
98     {
99         wxLogDebug(wxT("No CFMachPortForProcess"));
100         mach_port_deallocate(taskOfOurProcess, machPortForProcess);
101         return -1;
102     }
103     if(ShouldFreePort)
104     {
105         kernResult = mach_port_deallocate(taskOfOurProcess, machPortForProcess);
106         if(kernResult!=KERN_SUCCESS)
107         {
108             wxLogDebug(wxT("Couldn't deallocate mach port"));
109             return -1;
110         }
111     }
112     CFMachPortSetInvalidationCallBack(CFMachPortForProcess, &wxMAC_MachPortEndProcessDetect);
113     CFRunLoopSourceRef    runloopsource;
114     runloopsource = CFMachPortCreateRunLoopSource(NULL,CFMachPortForProcess, (CFIndex)0);
115     if(!runloopsource)
116     {
117         wxLogDebug(wxT("Couldn't create runloopsource"));
118         return -1;
119     }
120     
121     CFRelease(CFMachPortForProcess);
122
123     CFRunLoopAddSource(CFRunLoopGetCurrent(),runloopsource,kCFRunLoopDefaultMode);
124     CFRelease(runloopsource);
125     wxLogDebug(wxT("Successfully added notification to the runloop"));
126     return 0;
127 }
128 #endif
129
130 /*
131 #ifdef __GNUG__
132 #pragma implementation
133 #endif
134
135 #include "wx/utils.h"
136
137 #include "wx/process.h"
138 #include "wx/stream.h"
139
140 #include "wx/cocoa/string.h"
141
142 #import <Foundation/Foundation.h>
143 #import <AppKit/NSWorkspace.h>
144
145 //
146 // RN:  This is a prelimenary implementation - simple
147 // launching and process redirection works,
148 // but with the piping tests in the exec sample
149 // SIGPIPE is triggered...
150 //
151
152 class wxPipeInputStream : public wxInputStream
153 {
154 public:
155     wxPipeInputStream(NSPipe* thePipe) : 
156             m_thePipe(thePipe),
157             m_theHandle([m_thePipe fileHandleForReading])
158     {
159     }
160
161     ~wxPipeInputStream()
162     {   
163         [m_thePipe release];
164     }
165
166 protected:
167     virtual size_t OnSysRead(void *buffer, size_t size)
168     {
169         NSData* theData = [m_theHandle readDataOfLength:size];
170         memcpy(buffer, [theData bytes], [theData length]);
171         return [theData length];
172     }
173     
174     
175     NSPipe*             m_thePipe;
176     NSFileHandle*       m_theHandle;
177 };
178
179 class wxPipeOutputStream : public wxOutputStream
180 {
181 public:
182     wxPipeOutputStream(NSPipe* thePipe) : 
183             m_thePipe(thePipe),
184             m_theHandle([m_thePipe fileHandleForWriting])
185     {
186     }
187
188     ~wxPipeOutputStream()
189     {   
190         [m_thePipe release];
191     }
192
193 protected:
194
195     virtual size_t OnSysWrite(const void *buffer, size_t bufsize)
196     {
197         NSData* theData = [NSData dataWithBytesNoCopy:(void*)buffer 
198                                   length:bufsize];
199         [m_theHandle writeData:theData];
200         return bufsize;
201     }
202         
203     NSPipe*             m_thePipe;
204     NSFileHandle*       m_theHandle;
205 };
206
207 @interface wxTaskHandler : NSObject
208 {
209     long m_pid;
210     void* m_handle;
211 }
212 -(id)init:(void*)handle processIdentifier:(long)pid;
213 - (void)termHandler:(NSNotification *)aNotification;
214 @end
215
216 @implementation wxTaskHandler : NSObject
217
218 -(id)init:(void*)handle processIdentifier:(long)pid 
219 {
220     self = [super init];
221     
222     m_handle = handle;
223     m_pid = pid;
224
225     [[NSNotificationCenter defaultCenter] addObserver:self 
226             selector:@selector(termHandler:) 
227             name:NSTaskDidTerminateNotification 
228             object:nil];
229     return self;
230 }
231
232 - (void)termHandler:(NSNotification *)aNotification 
233 {
234     NSTask* theTask = [aNotification object];
235     
236     if ([theTask processIdentifier] == m_pid)
237     {
238         ((wxProcess*)m_handle)->OnTerminate([theTask processIdentifier], 
239                           [theTask terminationStatus]);
240         
241         [self release];
242     }
243 }
244
245 @end
246
247 long wxExecute(const wxString& command, 
248                 int sync, 
249                 wxProcess *handle)
250 {
251     NSTask* theTask = [[NSTask alloc] init];
252     
253     if (handle && handle->IsRedirected())
254     {
255         NSPipe* theStdinPipe = [[NSPipe alloc] init];
256         NSPipe* theStderrPipe = [[NSPipe alloc] init];
257         NSPipe* theStdoutPipe = [[NSPipe alloc] init];
258     
259         [theTask setStandardInput:theStdinPipe];
260         [theTask setStandardError:theStderrPipe];
261         [theTask setStandardOutput:theStdoutPipe];
262         
263         handle->SetPipeStreams(new wxPipeInputStream(theStdoutPipe),
264                                new wxPipeOutputStream(theStdinPipe),
265                                new wxPipeInputStream(theStderrPipe) );
266     }
267     
268     NSArray* theQuoteArguments = 
269         [wxNSStringWithWxString(command) componentsSeparatedByString:@"\""];
270         
271     NSMutableArray* theSeperatedArguments = 
272         [NSMutableArray arrayWithCapacity:10];
273         
274     for (unsigned i = 0; i < [theQuoteArguments count]; ++i)
275     {
276         [theSeperatedArguments addObjectsFromArray:
277             [[theQuoteArguments objectAtIndex:i] componentsSeparatedByString:@" "]
278         ];
279         
280         if(++i < [theQuoteArguments count])
281             [theSeperatedArguments addObject:[theQuoteArguments objectAtIndex:i]];
282     }
283     
284     [theTask setLaunchPath:[theSeperatedArguments objectAtIndex:0]];
285     [theTask setArguments:theSeperatedArguments];
286     [theTask launch];
287     
288     if(sync & wxEXEC_ASYNC)
289     {
290         [[wxTaskHandler alloc]init:handle 
291                               processIdentifier:[theTask processIdentifier]];
292                                 
293         return 0;
294     }
295     else
296     {
297         [theTask waitUntilExit];
298         
299         return [theTask terminationStatus];
300     }                      
301 }
302
303 */