Ticket #21718: main.c

File main.c, 13.2 KB (added by asgalon, 14 years ago)

Fixed compilation errors for 64 bit version... Runs on MacOS 10.6.2

Line 
1/*
2    Platypus - create MacOS X application bundles that execute scripts
3        This is the executable that goes into Platypus apps
4    Copyright (C) 2003 Sveinbjorn Thordarson <sveinbt@hi.is>
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20    main.c - main program file
21
22*/
23
24///////////////////////////////////////
25// Includes
26///////////////////////////////////////   
27#pragma mark Includes
28
29// Apple stuff
30#include <Carbon/Carbon.h>
31#include <CoreFoundation/CoreFoundation.h>
32
33// Unix stuff
34#include <string.h>
35#include <unistd.h>
36#include <sys/wait.h>
37#include <pthread.h>
38
39///////////////////////////////////////
40// Definitions
41///////////////////////////////////////   
42#pragma mark Definitions
43
44// name length limits
45#define kMaxPathLength 1024
46
47// names of files bundled with app
48#define kScriptFileName "script"
49#define kOpenDocFileName "openDoc"
50
51// custom carbon events
52#define kEventClassRedFatalAlert 911
53#define kEventKindX11Failed 911
54
55//maximum arguments the script accepts
56#define kMaxArgumentsToScript 252
57
58///////////////////////////////////////
59// Prototypes
60///////////////////////////////////////   
61#pragma mark Prototypes
62
63static void *Execute(void *arg);
64static void *OpenDoc(void *arg);
65static OSErr ExecuteScript(char *script, pid_t *pid);
66
67static void  GetParameters(void);
68static char* GetScript(void);
69static char* GetOpenDoc(void);
70
71OSErr LoadMenuBar(char *appName);
72
73static void RedFatalAlert(Str255 errorString, Str255 expStr);
74static short DoesFileExist(char *path);
75
76static OSErr AppQuitAEHandler(const AppleEvent *theAppleEvent,
77                              AppleEvent *reply, long refCon);
78static OSErr AppOpenDocAEHandler(const AppleEvent *theAppleEvent,
79                                 AppleEvent *reply, long refCon);
80static OSErr AppOpenAppAEHandler(const AppleEvent *theAppleEvent,
81                                 AppleEvent *reply, long refCon);
82static OSStatus X11FailedHandler(EventHandlerCallRef theHandlerCall,
83                                 EventRef theEvent, void *userData);
84
85///////////////////////////////////////
86// Globals
87///////////////////////////////////////   
88#pragma mark Globals
89
90// process id of forked process
91pid_t pid = 0;
92
93// thread id of threads that start scripts
94pthread_t odtid = 0, tid = 0;
95
96// indicator of whether the script has completed executing
97short taskDone = true;
98
99// execution parameters
100char scriptPath[kMaxPathLength];
101char openDocPath[kMaxPathLength];
102
103//arguments to the script
104char *arguments[kMaxArgumentsToScript+3];
105char *fileArgs[kMaxArgumentsToScript];
106short numArgs = 0;
107
108extern char **environ;
109
110#pragma mark -
111
112///////////////////////////////////////
113// Program entrance point
114///////////////////////////////////////
115int main(int argc, char* argv[])
116{
117    OSErr err = noErr;
118    EventTypeSpec events = { kEventClassRedFatalAlert, kEventKindX11Failed };
119
120    InitCursor();
121
122    //install Apple Event handlers
123    err += AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
124                                 NewAEEventHandlerUPP(AppQuitAEHandler),
125                                 0, false);
126    err += AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
127                                 NewAEEventHandlerUPP(AppOpenDocAEHandler),
128                                 0, false);
129    err += AEInstallEventHandler(kCoreEventClass, kAEOpenApplication,
130                                 NewAEEventHandlerUPP(AppOpenAppAEHandler),
131                                 0, false);
132    err += InstallEventHandler(GetApplicationEventTarget(),
133                               NewEventHandlerUPP(X11FailedHandler), 1,
134                               &events, NULL, NULL);
135
136    if (err) RedFatalAlert("\pInitialization Error",
137                           "\pError initing Apple Event handlers.");
138
139    //create the menu bar
140    if (err = LoadMenuBar(NULL)) RedFatalAlert("\pInitialization Error",
141                                               "\pError loading MenuBar.nib.");
142   
143    GetParameters(); //load data from files containing exec settings
144
145    RunApplicationEventLoop(); //Run the event loop
146    return 0;
147}
148                                 
149#pragma mark -
150
151///////////////////////////////////
152// Execution thread starts here
153///////////////////////////////////
154static void *Execute (void *arg)
155{
156    EventRef event;
157   
158    taskDone = false;
159    if (ExecuteScript(scriptPath, &pid) == (OSErr)11) {
160        CreateEvent(NULL, kEventClassRedFatalAlert, kEventKindX11Failed, 0,
161                    kEventAttributeNone, &event);
162        PostEventToQueue(GetMainEventQueue(), event, kEventPriorityStandard);
163    }
164    else ExitToShell();
165    return 0;
166}
167
168///////////////////////////////////
169// Open additional documents thread starts here
170///////////////////////////////////
171static void *OpenDoc (void *arg)
172{
173    ExecuteScript(openDocPath, NULL);
174    return 0;
175}
176
177///////////////////////////////////////
178// Run a script via the system command
179///////////////////////////////////////
180static OSErr ExecuteScript (char *script, pid_t *pid)
181{
182    pid_t wpid = 0, p = 0;
183    int status, i;
184   
185    if (! pid) pid = &p;
186   
187    // Generate the array of argument strings before we do any executing
188    arguments[0] = script;
189    for (i = 0; i < numArgs; i++) arguments[i + 1] = fileArgs[i];
190    arguments[i + 1] = NULL;
191
192    *pid = fork(); //open fork
193   
194    if (*pid == (pid_t)-1) exit(13); //error
195    else if (*pid == 0) { //child process started
196        execve(arguments[0], arguments, environ);
197        exit(13); //if we reach this point, there's an error
198    }
199
200    wpid = waitpid(*pid, &status, 0); //wait while child process finishes
201   
202    if (wpid == (pid_t)-1) return wpid;
203    return (OSErr)WEXITSTATUS(status);
204}
205
206#pragma mark -
207
208///////////////////////////////////////
209// This function loads all the neccesary settings
210// from config files in the Resources folder
211///////////////////////////////////////
212static void GetParameters (void)
213{
214    char *str;
215    if (! (str = (char *)GetScript())) //get path to script to be executed
216        RedFatalAlert("\pInitialization Error",
217                      "\pError getting script from application bundle.");
218    strcpy((char *)&scriptPath, str);
219   
220    if (! (str = (char *)GetOpenDoc())) //get path to openDoc
221        RedFatalAlert("\pInitialization Error",
222                      "\pError getting openDoc from application bundle.");
223    strcpy((char *)&openDocPath, str);
224}
225
226///////////////////////////////////////
227// Get path to the script in Resources folder
228///////////////////////////////////////
229static char* GetScript (void)
230{
231    CFStringRef fileName;
232    CFBundleRef appBundle;
233    CFURLRef scriptFileURL;
234    FSRef fileRef;
235    char *path;
236
237    //get CF URL for script
238    if (! (appBundle = CFBundleGetMainBundle())) return NULL;
239    if (! (fileName = CFStringCreateWithCString(NULL, kScriptFileName,
240                                                kCFStringEncodingASCII)))
241        return NULL;
242    if (! (scriptFileURL = CFBundleCopyResourceURL(appBundle, fileName, NULL,
243                                                   NULL))) return NULL;
244   
245    //Get file reference from Core Foundation URL
246    if (! CFURLGetFSRef(scriptFileURL, &fileRef)) return NULL;
247   
248    //dispose of the CF variables
249    CFRelease(scriptFileURL);
250    CFRelease(fileName);
251           
252    //create path string
253    if (! (path = malloc(kMaxPathLength))) return NULL;
254    if (FSRefMakePath(&fileRef, path, kMaxPathLength)) return NULL;
255    if (! DoesFileExist(path)) return NULL;
256   
257    return path;
258}
259
260///////////////////////////////////////
261// Gets the path to openDoc in Resources folder
262///////////////////////////////////////
263static char* GetOpenDoc (void)
264{
265    CFStringRef fileName;
266    CFBundleRef appBundle;
267    CFURLRef openDocFileURL;
268    FSRef fileRef;
269    char *path;
270   
271    //get CF URL for openDoc
272    if (! (appBundle = CFBundleGetMainBundle())) return NULL;
273    if (! (fileName = CFStringCreateWithCString(NULL, kOpenDocFileName,
274                                                kCFStringEncodingASCII)))
275        return NULL;
276    if (! (openDocFileURL = CFBundleCopyResourceURL(appBundle, fileName, NULL,
277                                                    NULL))) return NULL;
278   
279    //Get file reference from Core Foundation URL
280    if (! CFURLGetFSRef( openDocFileURL, &fileRef )) return NULL;
281   
282    //dispose of the CF variables
283    CFRelease(openDocFileURL);
284    CFRelease(fileName);
285       
286    //create path string
287    if (! (path = malloc(kMaxPathLength))) return NULL;
288    if (FSRefMakePath(&fileRef, path, kMaxPathLength)) return NULL;
289    if (! DoesFileExist(path)) return NULL;
290   
291    return path;
292}
293
294#pragma mark -
295
296/////////////////////////////////////
297// Load menu bar from nib
298/////////////////////////////////////
299OSErr LoadMenuBar (char *appName)
300{
301    OSErr err;
302    IBNibRef nibRef;
303   
304    if (err = CreateNibReference(CFSTR("MenuBar"), &nibRef)) return err;
305    if (err = SetMenuBarFromNib(nibRef, CFSTR("MenuBar"))) return err;
306    DisposeNibReference(nibRef);
307
308    return noErr;
309}
310
311#pragma mark -
312
313////////////////////////////////////////
314// Standard red error alert, then exit application
315////////////////////////////////////////
316static void RedFatalAlert (Str255 errorString, Str255 expStr)
317{
318//    StandardAlert(kAlertStopAlert, errorString,  expStr, NULL, NULL);
319    ExitToShell();
320}
321
322///////////////////////////////////////
323// Determines whether file exists at path or not
324///////////////////////////////////////
325static short DoesFileExist (char *path)
326{
327    if (access(path, F_OK) == -1) return false;
328    return true;       
329}
330
331#pragma mark -
332
333///////////////////////////////////////
334// Apple Event handler for Quit i.e. from
335// the dock or Application menu item
336///////////////////////////////////////
337static OSErr AppQuitAEHandler(const AppleEvent *theAppleEvent,
338                              AppleEvent *reply, long refCon)
339{
340    #pragma unused (reply, refCon, theAppleEvent)
341
342    while (numArgs > 0) free(fileArgs[numArgs--]);
343   
344    if (! taskDone && pid) { //kill the script process brutally
345        kill(pid, 9);
346        printf("Platypus App: PID %d killed brutally\n", pid);
347    }
348   
349    pthread_cancel(tid);
350    if (odtid) pthread_cancel(odtid);
351   
352    ExitToShell();
353   
354    return noErr;
355}
356
357/////////////////////////////////////
358// Handler for docs dragged on app icon
359/////////////////////////////////////
360static OSErr AppOpenDocAEHandler(const AppleEvent *theAppleEvent,
361                                 AppleEvent *reply, long refCon)
362{
363    #pragma unused (reply, refCon)
364       
365    OSErr err = noErr;
366    AEDescList fileSpecList;
367    AEKeyword keyword;
368    DescType type;
369       
370    short i;
371    long count, actualSize;
372       
373    FSRef fileRef;
374    char path[kMaxPathLength];
375   
376    while (numArgs > 0) free(fileArgs[numArgs--]);
377       
378    //Read the AppleEvent
379    err = AEGetParamDesc(theAppleEvent, keyDirectObject, typeAEList,
380                         &fileSpecList);
381               
382    err = AECountItems(&fileSpecList, &count); //Count number of files
383               
384    for (i = 1; i <= count; i++) { //iteratively process each file
385        //get fsref from apple event
386        if (! (err = AEGetNthPtr(&fileSpecList, i, typeFSRef, &keyword, &type,
387                                 (Ptr)&fileRef, sizeof(FSRef), &actualSize)))
388        {
389            //get path from file spec
390            if ((err = FSRefMakePath(&fileRef, (unsigned char *)&path,
391                                  kMaxPathLength))) return err;
392                           
393            if (numArgs == kMaxArgumentsToScript) break;
394
395            if (! (fileArgs[numArgs] = malloc(kMaxPathLength))) return true;
396
397            strcpy(fileArgs[numArgs++], (char *)&path);
398        }
399        else return err;
400    }
401       
402    if (! taskDone) pthread_create(&odtid, NULL, OpenDoc, NULL);
403    else pthread_create(&tid, NULL, Execute, NULL);
404       
405    return err;
406}
407
408///////////////////////////////
409// Handler for clicking on app icon
410///////////////////////////////
411static OSErr AppOpenAppAEHandler(const AppleEvent *theAppleEvent,
412                                 AppleEvent *reply, long refCon)
413{
414    #pragma unused (reply, refCon, theAppleEvent)
415       
416    // the app has been opened without any items dragged on to it
417    pthread_create(&tid, NULL, Execute, NULL);
418}
419
420//////////////////////////////////
421// Handler for when X11 fails to start
422//////////////////////////////////
423static OSStatus X11FailedHandler(EventHandlerCallRef theHandlerCall, 
424                                 EventRef theEvent, void *userData)
425{
426    #pragma unused(theHanderCall, theEvent, userData)
427
428    pthread_join(tid, NULL);
429    if (odtid) pthread_join(odtid, NULL);
430
431    RedFatalAlert("\pFailed to start X11",
432                  "\pGimp.app requires Apple's X11.");
433
434    return noErr;
435}