1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        mac/corefoundation/utilsexc_base.cpp
3 // Purpose:     wxMacExecute
4 // Author:      Ryan Norton
5 // Modified by:
6 // Created:     2005-06-21
7 // RCS-ID:      $Id: utilsexc_base.cpp 40283 2006-07-24 18:01:39Z VZ $
8 // Copyright:   (c) Ryan Norton
9 // Licence:     wxWindows licence
10 // Notes:       Source was originally in utilsexc_cf.cpp,1.6 then moved
11 //              to totally unrelated hid.cpp,1.8.
12 /////////////////////////////////////////////////////////////////////////////
13 
14 //===========================================================================
15 //  DECLARATIONS
16 //===========================================================================
17 
18 //---------------------------------------------------------------------------
19 // Pre-compiled header stuff
20 //---------------------------------------------------------------------------
21 
22 // For compilers that support precompilation, includes "wx.h".
23 #include "wx/wxprec.h"
24 
25 // WX includes
26 #ifndef WX_PRECOMP
27     #include "wx/string.h"
28     #include "wx/log.h"
29     #include "wx/intl.h"
30     #include "wx/utils.h"
31 #endif // WX_PRECOMP
32 
33 // Mac Includes
34 #include <CoreFoundation/CoreFoundation.h>
35 #include <ApplicationServices/ApplicationServices.h>
36 
37 // More WX Includes
38 #include "wx/filename.h"
39 #include "wx/mac/corefoundation/cfstring.h"
40 
41 // Default path style
42 #ifdef __WXMAC_OSX__
43 #define kDefaultPathStyle kCFURLPOSIXPathStyle
44 #else
45 #define kDefaultPathStyle kCFURLHFSPathStyle
46 #endif
47 
48 //===========================================================================
49 //  IMPLEMENTATION
50 //===========================================================================
51 
52 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
53 //
54 //    wxMacExecute
55 //
56 // argv is the command line split up, with the application path first
57 // flags are the flags from wxExecute
58 // process is the process passed from wxExecute for pipe streams etc.
59 // returns -1 on error for wxEXEC_SYNC and 0 on error for wxEXEC_ASYNC
60 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
wxMacExecute(wxChar ** argv,int flags,wxProcess * process)61 long wxMacExecute(wxChar **argv,
62                int flags,
63                wxProcess *process)
64 {
65     // Semi-macros used for return value of wxMacExecute
66     const long errorCode = ((flags & wxEXEC_SYNC) ? -1 : 0);
67     const long successCode = ((flags & wxEXEC_SYNC) ? 0 : -1); // fake PID
68 
69     // Obtains the number of arguments for determining the size of
70     // the CFArray used to hold them
71     CFIndex cfiCount = 0;
72     for(wxChar** argvcopy = argv; *argvcopy != NULL ; ++argvcopy)
73     {
74         ++cfiCount;
75     }
76 
77     // If there is not a single argument then there is no application
78     // to launch
79     if(cfiCount == 0)
80     {
81         wxLogDebug(wxT("wxMacExecute No file to launch!"));
82         return errorCode ;
83     }
84 
85     // Path to bundle
86     wxString path = *argv++;
87 
88     // Create a CFURL for the application path
89     // Created this way because we are opening a bundle which is a directory
90     CFURLRef cfurlApp =
91         CFURLCreateWithFileSystemPath(
92             kCFAllocatorDefault,
93             wxMacCFStringHolder(path),
94             kDefaultPathStyle,
95             true); //false == not a directory
96 
97     // Check for error from the CFURL
98     if(!cfurlApp)
99     {
100         wxLogDebug(wxT("wxMacExecute Can't open path: %s"), path.c_str());
101         return errorCode ;
102     }
103 
104     // Create a CFBundle from the CFURL created earlier
105     CFBundleRef cfbApp = CFBundleCreate(kCFAllocatorDefault, cfurlApp);
106 
107     // Check to see if CFBundleCreate returned an error,
108     // and if it did this was an invalid bundle or not a bundle
109     // at all (maybe a simple directory etc.)
110     if(!cfbApp)
111     {
112         wxLogDebug(wxT("wxMacExecute Bad bundle: %s"), path.c_str());
113         CFRelease(cfurlApp);
114         return errorCode ;
115     }
116 
117     // Get the bundle type and make sure its an 'APPL' bundle
118     // Otherwise we're dealing with something else here...
119     UInt32 dwBundleType, dwBundleCreator;
120     CFBundleGetPackageInfo(cfbApp, &dwBundleType, &dwBundleCreator);
121     if(dwBundleType != 'APPL')
122     {
123         wxLogDebug(wxT("wxMacExecute Not an APPL bundle: %s"), path.c_str());
124         CFRelease(cfbApp);
125         CFRelease(cfurlApp);
126         return errorCode ;
127     }
128 
129     // Create a CFArray for dealing with the command line
130     // arguments to the bundle
131     CFMutableArrayRef cfaFiles = CFArrayCreateMutable(kCFAllocatorDefault,
132                                     cfiCount-1, &kCFTypeArrayCallBacks);
133     if(!cfaFiles) //This should never happen
134     {
135         wxLogDebug(wxT("wxMacExecute Could not create CFMutableArray"));
136         CFRelease(cfbApp);
137         CFRelease(cfurlApp);
138         return errorCode ;
139     }
140 
141     // Loop through command line arguments to the bundle,
142     // turn them into CFURLs and then put them in cfaFiles
143     // For use to launch services call
144         for( ; *argv != NULL ; ++argv)
145         {
146         // Check for '<' as this will ring true for
147         // CFURLCreateWithString but is generally not considered
148         // typical on mac but is usually passed here from wxExecute
149         if (wxStrcmp(*argv, wxT("<")) == 0)
150             continue;
151 
152 
153         CFURLRef cfurlCurrentFile;    // CFURL to hold file path
154         wxFileName argfn(*argv);     // Filename for path
155 
156         if(argfn.DirExists())
157         {
158             // First, try creating as a directory
159             cfurlCurrentFile = CFURLCreateWithFileSystemPath(
160                                 kCFAllocatorDefault,
161                                 wxMacCFStringHolder(*argv),
162                                 kDefaultPathStyle,
163                                 true); //true == directory
164         }
165         else if(argfn.FileExists())
166         {
167             // And if it isn't a directory try creating it
168             // as a regular file
169             cfurlCurrentFile = CFURLCreateWithFileSystemPath(
170                                 kCFAllocatorDefault,
171                                 wxMacCFStringHolder(*argv),
172                                 kDefaultPathStyle,
173                                 false); //false == regular file
174         }
175         else
176         {
177             // Argument did not refer to
178             // an entry in the local filesystem,
179             // so try creating it through CFURLCreateWithString
180             cfurlCurrentFile = CFURLCreateWithString(
181                                 kCFAllocatorDefault,
182                                 wxMacCFStringHolder(*argv),
183                                 NULL);
184         }
185 
186         // Continue in the loop if the CFURL could not be created
187         if(!cfurlCurrentFile)
188         {
189             wxLogDebug(
190                 wxT("wxMacExecute Could not create CFURL for argument:%s"),
191                 *argv);
192             continue;
193         }
194 
195         // Add the valid CFURL to the argument array and then
196         // release it as the CFArray adds a ref count to it
197             CFArrayAppendValue(
198                 cfaFiles,
199                 cfurlCurrentFile
200                             );
201             CFRelease(cfurlCurrentFile); // array has retained it
202         }
203 
204     // Create a LSLaunchURLSpec for use with LSOpenFromURLSpec
205     // Note that there are several flag options (launchFlags) such
206     // as kLSLaunchDontSwitch etc. and maybe we could be more
207     // picky about the flags we choose
208     LSLaunchURLSpec launchspec;
209     launchspec.appURL = cfurlApp;
210     launchspec.itemURLs = cfaFiles;
211     launchspec.passThruParams = NULL; //AEDesc*
212     launchspec.launchFlags = kLSLaunchDefaults;
213     launchspec.asyncRefCon = NULL;
214 
215     // Finally, call LSOpenFromURL spec with our arguments
216     // 2nd parameter is a pointer to a CFURL that gets
217     // the actual path launched by the function
218     OSStatus status = LSOpenFromURLSpec(&launchspec, NULL);
219 
220     // Cleanup corefoundation references
221     CFRelease(cfbApp);
222     CFRelease(cfurlApp);
223     CFRelease(cfaFiles);
224 
225     // Check for error from LSOpenFromURLSpec
226     if(status != noErr)
227     {
228         wxLogDebug(wxT("wxMacExecute LSOpenFromURLSpec Error: %d"),
229                    (int)status);
230         return errorCode ;
231     }
232 
233     // No error from LSOpenFromURLSpec, so app was launched
234     return successCode;
235 }
236 
237