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>
6 With modifications by Aaron Voisine for gimp.app
7 With modifications by Marianne gagnon for Wilber-loves-apple
8 With modifications by Michael Wybrow for Inkscape.app
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 main.c - main program file
26 */
28 /*
29 * This app laucher basically takes care of:
30 * - launching Inkscape and X11 when double-clicked
31 * - bringing X11 to the top when its icon is clicked in the dock (via a small applescript)
32 * - catch file dropped on icon events (and double-clicked gimp documents) and notify gimp.
33 * - catch quit events performed outside gimp, e.g. on the dock icon.
34 */
36 ///////////////////////////////////////
37 // Includes
38 ///////////////////////////////////////
39 #pragma mark Includes
41 // Apple stuff
42 #include <Carbon/Carbon.h>
43 #include <CoreFoundation/CoreFoundation.h>
44 #include <Security/Authorization.h>
45 #include <Security/AuthorizationTags.h>
47 // Unix stuff
48 #include <string.h>
49 #include <unistd.h>
50 #include <sys/wait.h>
51 #include <pthread.h>
52 #include <stdio.h>
54 ///////////////////////////////////////
55 // Definitions
56 ///////////////////////////////////////
57 #pragma mark Definitions
59 // name length limits
60 #define kMaxPathLength 1024
62 // names of files bundled with app
63 #define kScriptFileName "script"
64 #define kOpenDocFileName "openDoc"
66 // custom carbon event class
67 #define kEventClassRedFatalAlert 911
69 // custom carbon event types
70 #define kEventKindX11Failed 911
71 #define kEventKindFCCacheFailed 912
73 //maximum arguments the script accepts
74 #define kMaxArgumentsToScript 252
76 ///////////////////////////////////////
77 // Prototypes
78 ///////////////////////////////////////
79 #pragma mark Prototypes
81 static void *Execute(void *arg);
82 static void *OpenDoc(void *arg);
83 static OSErr ExecuteScript(char *script, pid_t *pid);
85 static void GetParameters(void);
86 static char* GetScript(void);
87 static char* GetOpenDoc(void);
89 OSErr LoadMenuBar(char *appName);
91 static OSStatus FSMakePath(FSSpec file, char *path, long maxPathSize);
92 static void RedFatalAlert(Str255 errorString, Str255 expStr);
93 static short DoesFileExist(char *path);
94 static OSStatus FixFCCache(void);
96 static OSErr AppQuitAEHandler(const AppleEvent *theAppleEvent,
97 AppleEvent *reply, long refCon);
98 static OSErr AppOpenDocAEHandler(const AppleEvent *theAppleEvent,
99 AppleEvent *reply, long refCon);
100 static OSErr AppOpenAppAEHandler(const AppleEvent *theAppleEvent,
101 AppleEvent *reply, long refCon);
102 static OSStatus X11FailedHandler(EventHandlerCallRef theHandlerCall,
103 EventRef theEvent, void *userData);
104 static OSStatus FCCacheFailedHandler(EventHandlerCallRef theHandlerCall,
105 EventRef theEvent, void *userData);
106 static OSErr AppReopenAppAEHandler(const AppleEvent *theAppleEvent,
107 AppleEvent *reply, long refCon);
109 static OSStatus CompileAppleScript(const void* text, long textLength,
110 AEDesc *resultData);
111 static OSStatus SimpleCompileAppleScript(const char* theScript);
112 static void runScript();
114 ///////////////////////////////////////
115 // Globals
116 ///////////////////////////////////////
117 #pragma mark Globals
119 // process id of forked process
120 pid_t pid = 0;
122 // thread id of threads that start scripts
123 pthread_t odtid = 0, tid = 0;
125 // indicator of whether the script has completed executing
126 short taskDone = true;
128 // execution parameters
129 char scriptPath[kMaxPathLength];
130 char openDocPath[kMaxPathLength];
132 //arguments to the script
133 char *arguments[kMaxArgumentsToScript+3];
134 char *fileArgs[kMaxArgumentsToScript];
135 short numArgs = 0;
137 extern char **environ;
139 #pragma mark -
141 ///////////////////////////////////////
142 // Program entrance point
143 ///////////////////////////////////////
144 int main(int argc, char* argv[])
145 {
146 OSErr err = noErr;
147 EventTypeSpec X11events = { kEventClassRedFatalAlert, kEventKindX11Failed };
148 EventTypeSpec FCCacheEvents = { kEventClassRedFatalAlert, kEventKindFCCacheFailed };
150 InitCursor();
152 //install Apple Event handlers
153 err += AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
154 NewAEEventHandlerUPP(AppQuitAEHandler),
155 0, false);
156 err += AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
157 NewAEEventHandlerUPP(AppOpenDocAEHandler),
158 0, false);
159 err += AEInstallEventHandler(kCoreEventClass, kAEOpenApplication,
160 NewAEEventHandlerUPP(AppOpenAppAEHandler),
161 0, false);
163 err += AEInstallEventHandler(kCoreEventClass, kAEReopenApplication,
164 NewAEEventHandlerUPP(AppReopenAppAEHandler),
165 0, false);
167 err += InstallEventHandler(GetApplicationEventTarget(),
168 NewEventHandlerUPP(X11FailedHandler), 1,
169 &X11events, NULL, NULL);
170 err += InstallEventHandler(GetApplicationEventTarget(),
171 NewEventHandlerUPP(FCCacheFailedHandler), 1,
172 &FCCacheEvents, NULL, NULL);
174 if (err) RedFatalAlert("\pInitialization Error",
175 "\pError initing Apple Event handlers.");
177 //create the menu bar
178 if (err = LoadMenuBar(NULL)) RedFatalAlert("\pInitialization Error",
179 "\pError loading MenuBar.nib.");
181 GetParameters(); //load data from files containing exec settings
183 // compile "icon clicked" script so it's ready to execute
184 SimpleCompileAppleScript("tell application \"X11\" to activate");
186 RunApplicationEventLoop(); //Run the event loop
187 return 0;
188 }
190 #pragma mark -
193 static void RequestUserAttention(void)
194 {
195 NMRecPtr notificationRequest = (NMRecPtr) NewPtr(sizeof(NMRec));
197 memset(notificationRequest, 0, sizeof(*notificationRequest));
198 notificationRequest->qType = nmType;
199 notificationRequest->nmMark = 1;
200 notificationRequest->nmIcon = 0;
201 notificationRequest->nmSound = 0;
202 notificationRequest->nmStr = NULL;
203 notificationRequest->nmResp = NULL;
205 verify_noerr(NMInstall(notificationRequest));
206 }
209 static void ShowFirstStartWarningDialog(void)
210 {
211 SInt16 itemHit;
213 AlertStdAlertParamRec params;
214 params.movable = true;
215 params.helpButton = false;
216 params.filterProc = NULL;
217 params.defaultText = (void *) kAlertDefaultOKText;
218 params.cancelText = NULL;
219 params.otherText = NULL;
220 params.defaultButton = kAlertStdAlertOKButton;
221 params.cancelButton = kAlertStdAlertCancelButton;
222 params.position = kWindowDefaultPosition;
224 StandardAlert(kAlertNoteAlert, "\pInkscape on Mac OS X",
225 "\pWhile Inkscape is open, its windows can be displayed or hidden by displaying or hiding the X11 application.\n\nThe first time this version of Inkscape is run it may take several minutes before the main window is displayed while font caches are built.",
226 ¶ms, &itemHit);
227 }
230 //////////////////////////////////
231 // Handler for when fontconfig caches need to be generated
232 //////////////////////////////////
233 static OSStatus FCCacheFailedHandler(EventHandlerCallRef theHandlerCall,
234 EventRef theEvent, void *userData)
235 {
237 pthread_join(tid, NULL);
238 if (odtid) pthread_join(odtid, NULL);
240 // Bounce Inkscape Dock icon
241 RequestUserAttention();
242 // Need to show warning to the user, then carry on.
243 ShowFirstStartWarningDialog();
245 // Note that we've seen the warning.
246 system("test -d \"$HOME/.inkscape-etc\" || mkdir -p \"$HOME/.inkscape-etc\"; "
247 "touch \"$HOME/.inkscape-etc/.fccache-new\"");
248 // Rerun now.
249 OSErr err = ExecuteScript(scriptPath, &pid);
250 ExitToShell();
252 return noErr;
253 }
256 static size_t safeRead(int d, void *buf, size_t nbytes)
257 {
258 ssize_t bytesToRead = nbytes;
259 ssize_t bytesRead = 0;
260 char *offset = (char *) buf;
262 while ((bytesToRead > 0))
263 {
264 bytesRead = read(d, offset, bytesToRead);
265 if (bytesRead > 0)
266 {
267 offset += bytesRead;
268 bytesToRead -= bytesRead;
269 }
270 else if (bytesRead == 0)
271 {
272 // Reached EOF.
273 break;
274 }
275 else if (bytesRead == -1)
276 {
277 if ((errno == EINTR) || (errno == EAGAIN))
278 {
279 // Try again.
280 continue;
281 }
282 return 0;
283 }
284 }
285 return bytesRead;
286 }
289 /////////////////////////////////////
290 // Code to run fc-cache on first run
291 /////////////////////////////////////
292 static OSStatus FixFCCache (void)
293 {
294 FILE *fileConnToChild = NULL;
295 int fdConnToChild = 0;
296 pid_t childPID = WAIT_ANY;
297 size_t bytesChildPID;
298 size_t bytesRead;
299 int status;
301 char commandStr[] = "/usr/X11R6/bin/fc-cache";
302 char *commandArgs[] = { "-f", NULL };
304 // Run fc-cache
305 AuthorizationItem authItems[] =
306 {
307 {
308 kAuthorizationRightExecute,
309 strlen(commandStr),
310 commandStr,
311 0
312 }
313 };
314 AuthorizationItemSet authItemSet =
315 {
316 1,
317 authItems
318 };
319 AuthorizationRef authRef = NULL;
320 OSStatus err = AuthorizationCreate (NULL, &authItemSet,
321 kAuthorizationFlagInteractionAllowed |
322 kAuthorizationFlagExtendRights, &authRef);
324 if (err == errAuthorizationSuccess)
325 {
326 err = AuthorizationExecuteWithPrivileges(authRef, commandStr,
327 kAuthorizationFlagDefaults, commandArgs,
328 &fileConnToChild);
330 if (err == errAuthorizationSuccess)
331 {
332 // Unfortunately, AuthorizationExecuteWithPrivileges
333 // does not return the process ID associated with the
334 // process it runs. The best solution we have it to
335 // try and get the process ID from the file descriptor.
336 // This is based on example code from Apple's
337 // MoreAuthSample.
339 fdConnToChild = fileno(fileConnToChild);
341 // Try an get the process ID of the fc-cache command
342 bytesChildPID = sizeof(childPID);
343 bytesRead = safeRead(fdConnToChild, &childPID,
344 bytesChildPID);
345 if (bytesRead != bytesChildPID)
346 {
347 // If we can't get it the best alternative
348 // is to wait for any child to finish.
349 childPID = WAIT_ANY;
350 }
352 if (fileConnToChild != NULL) {
353 fclose(fileConnToChild);
354 }
356 // Wait for child process to finish.
357 waitpid(childPID, &status, 0);
358 }
359 }
360 AuthorizationFree(authRef, kAuthorizationFlagDestroyRights);
362 return err;
363 }
366 ///////////////////////////////////
367 // Execution thread starts here
368 ///////////////////////////////////
369 static void *Execute (void *arg)
370 {
371 EventRef event;
373 taskDone = false;
375 OSErr err = ExecuteScript(scriptPath, &pid);
376 if (err == (OSErr)11) {
377 CreateEvent(NULL, kEventClassRedFatalAlert, kEventKindX11Failed, 0,
378 kEventAttributeNone, &event);
379 PostEventToQueue(GetMainEventQueue(), event, kEventPriorityStandard);
380 }
381 else if (err == (OSErr)12) {
382 CreateEvent(NULL, kEventClassRedFatalAlert, kEventKindFCCacheFailed, 0,
383 kEventAttributeNone, &event);
384 PostEventToQueue(GetMainEventQueue(), event, kEventPriorityHigh);
385 }
386 else ExitToShell();
387 return 0;
388 }
390 ///////////////////////////////////
391 // Open additional documents thread starts here
392 ///////////////////////////////////
393 static void *OpenDoc (void *arg)
394 {
395 ExecuteScript(openDocPath, NULL);
396 return 0;
397 }
399 ///////////////////////////////////////
400 // Run a script via the system command
401 ///////////////////////////////////////
402 static OSErr ExecuteScript (char *script, pid_t *pid)
403 {
404 pid_t wpid = 0, p = 0;
405 int status, i;
407 if (! pid) pid = &p;
409 // Generate the array of argument strings before we do any executing
410 arguments[0] = script;
411 for (i = 0; i < numArgs; i++) arguments[i + 1] = fileArgs[i];
412 arguments[i + 1] = NULL;
414 *pid = fork(); //open fork
416 if (*pid == (pid_t)-1) exit(13); //error
417 else if (*pid == 0) { //child process started
418 execve(arguments[0], arguments, environ);
419 exit(13); //if we reach this point, there's an error
420 }
422 wpid = waitpid(*pid, &status, 0); //wait while child process finishes
424 if (wpid == (pid_t)-1) return wpid;
425 return (OSErr)WEXITSTATUS(status);
426 }
428 #pragma mark -
430 ///////////////////////////////////////
431 // This function loads all the neccesary settings
432 // from config files in the Resources folder
433 ///////////////////////////////////////
434 static void GetParameters (void)
435 {
436 char *str;
437 if (! (str = (char *)GetScript())) //get path to script to be executed
438 RedFatalAlert("\pInitialization Error",
439 "\pError getting script from application bundle.");
440 strcpy((char *)&scriptPath, str);
442 if (! (str = (char *)GetOpenDoc())) //get path to openDoc
443 RedFatalAlert("\pInitialization Error",
444 "\pError getting openDoc from application bundle.");
445 strcpy((char *)&openDocPath, str);
446 }
448 ///////////////////////////////////////
449 // Get path to the script in Resources folder
450 ///////////////////////////////////////
451 static char* GetScript (void)
452 {
453 CFStringRef fileName;
454 CFBundleRef appBundle;
455 CFURLRef scriptFileURL;
456 FSRef fileRef;
457 FSSpec fileSpec;
458 char *path;
460 //get CF URL for script
461 if (! (appBundle = CFBundleGetMainBundle())) return NULL;
462 if (! (fileName = CFStringCreateWithCString(NULL, kScriptFileName,
463 kCFStringEncodingASCII)))
464 return NULL;
465 if (! (scriptFileURL = CFBundleCopyResourceURL(appBundle, fileName, NULL,
466 NULL))) return NULL;
468 //Get file reference from Core Foundation URL
469 if (! CFURLGetFSRef(scriptFileURL, &fileRef)) return NULL;
471 //dispose of the CF variables
472 CFRelease(scriptFileURL);
473 CFRelease(fileName);
475 //convert FSRef to FSSpec
476 if (FSGetCatalogInfo(&fileRef, kFSCatInfoNone, NULL, NULL, &fileSpec,
477 NULL)) return NULL;
479 //create path string
480 if (! (path = malloc(kMaxPathLength))) return NULL;
481 if (FSMakePath(fileSpec, path, kMaxPathLength)) return NULL;
482 if (! DoesFileExist(path)) return NULL;
484 return path;
485 }
487 ///////////////////////////////////////
488 // Gets the path to openDoc in Resources folder
489 ///////////////////////////////////////
490 static char* GetOpenDoc (void)
491 {
492 CFStringRef fileName;
493 CFBundleRef appBundle;
494 CFURLRef openDocFileURL;
495 FSRef fileRef;
496 FSSpec fileSpec;
497 char *path;
499 //get CF URL for openDoc
500 if (! (appBundle = CFBundleGetMainBundle())) return NULL;
501 if (! (fileName = CFStringCreateWithCString(NULL, kOpenDocFileName,
502 kCFStringEncodingASCII)))
503 return NULL;
504 if (! (openDocFileURL = CFBundleCopyResourceURL(appBundle, fileName, NULL,
505 NULL))) return NULL;
507 //Get file reference from Core Foundation URL
508 if (! CFURLGetFSRef( openDocFileURL, &fileRef )) return NULL;
510 //dispose of the CF variables
511 CFRelease(openDocFileURL);
512 CFRelease(fileName);
514 //convert FSRef to FSSpec
515 if (FSGetCatalogInfo(&fileRef, kFSCatInfoNone, NULL, NULL, &fileSpec,
516 NULL)) return NULL;
518 //create path string
519 if (! (path = malloc(kMaxPathLength))) return NULL;
520 if (FSMakePath(fileSpec, path, kMaxPathLength)) return NULL;
521 if (! DoesFileExist(path)) return NULL;
523 return path;
524 }
526 #pragma mark -
528 /////////////////////////////////////
529 // Load menu bar from nib
530 /////////////////////////////////////
531 OSErr LoadMenuBar (char *appName)
532 {
533 OSErr err;
534 IBNibRef nibRef;
536 if (err = CreateNibReference(CFSTR("MenuBar"), &nibRef)) return err;
537 if (err = SetMenuBarFromNib(nibRef, CFSTR("MenuBar"))) return err;
538 DisposeNibReference(nibRef);
540 return noErr;
541 }
543 #pragma mark -
545 ///////////////////////////////////////
546 // Generate path string from FSSpec record
547 ///////////////////////////////////////
548 static OSStatus FSMakePath(FSSpec file, char *path, long maxPathSize)
549 {
550 OSErr err = noErr;
551 FSRef fileRef;
553 //create file reference from file spec
554 if (err = FSpMakeFSRef(&file, &fileRef)) return err;
556 // and then convert the FSRef to a path
557 return FSRefMakePath(&fileRef, path, maxPathSize);
558 }
560 ////////////////////////////////////////
561 // Standard red error alert, then exit application
562 ////////////////////////////////////////
563 static void RedFatalAlert (Str255 errorString, Str255 expStr)
564 {
565 StandardAlert(kAlertStopAlert, errorString, expStr, NULL, NULL);
566 ExitToShell();
567 }
569 ///////////////////////////////////////
570 // Determines whether file exists at path or not
571 ///////////////////////////////////////
572 static short DoesFileExist (char *path)
573 {
574 if (access(path, F_OK) == -1) return false;
575 return true;
576 }
578 #pragma mark -
580 ///////////////////////////////////////
581 // Apple Event handler for Quit i.e. from
582 // the dock or Application menu item
583 ///////////////////////////////////////
584 static OSErr AppQuitAEHandler(const AppleEvent *theAppleEvent,
585 AppleEvent *reply, long refCon)
586 {
587 #pragma unused (reply, refCon, theAppleEvent)
589 while (numArgs > 0) free(fileArgs[numArgs--]);
591 if (! taskDone && pid) { //kill the script process brutally
592 kill(pid, 9);
593 printf("Platypus App: PID %d killed brutally\n", pid);
594 }
596 pthread_cancel(tid);
597 if (odtid) pthread_cancel(odtid);
599 ExitToShell();
601 return noErr;
602 }
604 /////////////////////////////////////
605 // Handler for docs dragged on app icon
606 /////////////////////////////////////
607 static OSErr AppOpenDocAEHandler(const AppleEvent *theAppleEvent,
608 AppleEvent *reply, long refCon)
609 {
610 #pragma unused (reply, refCon)
612 OSErr err = noErr;
613 AEDescList fileSpecList;
614 AEKeyword keyword;
615 DescType type;
617 short i;
618 long count, actualSize;
620 FSSpec fileSpec;
621 char path[kMaxPathLength];
623 while (numArgs > 0) free(fileArgs[numArgs--]);
625 //Read the AppleEvent
626 err = AEGetParamDesc(theAppleEvent, keyDirectObject, typeAEList,
627 &fileSpecList);
629 err = AECountItems(&fileSpecList, &count); //Count number of files
631 for (i = 1; i <= count; i++) { //iteratively process each file
632 //get fsspec from apple event
633 if (! (err = AEGetNthPtr(&fileSpecList, i, typeFSS, &keyword, &type,
634 (Ptr)&fileSpec, sizeof(FSSpec), &actualSize)))
635 {
636 //get path from file spec
637 if ((err = FSMakePath(fileSpec, (unsigned char *)&path,
638 kMaxPathLength))) return err;
640 if (numArgs == kMaxArgumentsToScript) break;
642 if (! (fileArgs[numArgs] = malloc(kMaxPathLength))) return true;
644 strcpy(fileArgs[numArgs++], (char *)&path);
645 }
646 else return err;
647 }
649 if (! taskDone) pthread_create(&odtid, NULL, OpenDoc, NULL);
650 else pthread_create(&tid, NULL, Execute, NULL);
652 return err;
653 }
655 ///////////////////////////////
656 // Handler for clicking on app icon
657 ///////////////////////////////
658 // if app is already open
659 static OSErr AppReopenAppAEHandler(const AppleEvent *theAppleEvent,
660 AppleEvent *reply, long refCon)
661 {
662 runScript();
663 }
665 // if app is being opened
666 static OSErr AppOpenAppAEHandler(const AppleEvent *theAppleEvent,
667 AppleEvent *reply, long refCon)
668 {
669 #pragma unused (reply, refCon, theAppleEvent)
671 // the app has been opened without any items dragged on to it
672 pthread_create(&tid, NULL, Execute, NULL);
674 return noErr;
675 }
678 static void OpenURL(Str255 url)
679 {
680 // Use Internet Config to hand the URL to the appropriate application, as
681 // set by the user in the Internet Preferences pane.
682 ICInstance icInstance;
683 // Applications creator code:
684 OSType signature = 'Inks';
685 OSStatus error = ICStart( &icInstance, signature );
686 if ( error == noErr )
687 {
688 ConstStr255Param hint = 0x0;
689 const char* data = url;
690 long length = strlen(url);
691 long start = 0;
692 long end = length;
693 // Don't bother testing return value (error); launched application will
694 // report problems.
695 ICLaunchURL( icInstance, hint, data, length, &start, &end );
696 ICStop( icInstance );
697 }
698 }
701 //////////////////////////////////
702 // Handler for when X11 fails to start
703 //////////////////////////////////
704 static OSStatus X11FailedHandler(EventHandlerCallRef theHandlerCall,
705 EventRef theEvent, void *userData)
706 {
707 #pragma unused(theHanderCall, theEvent, userData)
709 pthread_join(tid, NULL);
710 if (odtid) pthread_join(odtid, NULL);
712 SInt16 itemHit;
713 const char *getX11 = "\pGet X11 for Panther";
715 AlertStdAlertParamRec params;
716 params.movable = true;
717 params.helpButton = false;
718 params.filterProc = NULL;
719 params.defaultText = (StringPtr) kAlertDefaultOKText;
720 params.cancelText = getX11;
721 params.otherText = NULL;
722 params.defaultButton = kAlertStdAlertOKButton;
723 params.cancelButton = kAlertStdAlertCancelButton;
724 params.position = kWindowDefaultPosition;
726 StandardAlert(kAlertStopAlert, "\pFailed to start X11",
727 "\pInkscape.app requires Apple's X11, which is freely downloadable from Apple's website for Panther (10.3.x) users and available as an optional install from the installation DVD for Tiger (10.4.x) users.\n\nPlease install X11 and restart Inkscape.",
728 ¶ms, &itemHit);
730 if (itemHit == kAlertStdAlertCancelButton)
731 {
732 OpenURL("http://www.apple.com/downloads/macosx/apple/macosx_updates/x11formacosx.html");
733 }
735 ExitToShell();
738 return noErr;
739 }
742 // Compile and run a small AppleScript. The code below does no cleanup and no proper error checks
743 // but since it's there until the app is shut down, and since we know the script is okay,
744 // there should not be any problems.
745 ComponentInstance theComponent;
746 AEDesc scriptTextDesc;
747 OSStatus err;
748 OSAID scriptID, resultID;
750 static OSStatus CompileAppleScript(const void* text, long textLength,
751 AEDesc *resultData) {
753 resultData = NULL;
754 /* set up locals to a known state */
755 theComponent = NULL;
756 AECreateDesc(typeNull, NULL, 0, &scriptTextDesc);
757 scriptID = kOSANullScript;
758 resultID = kOSANullScript;
760 /* open the scripting component */
761 theComponent = OpenDefaultComponent(kOSAComponentType,
762 typeAppleScript);
763 if (theComponent == NULL) { err = paramErr; return err; }
765 /* put the script text into an aedesc */
766 err = AECreateDesc(typeChar, text, textLength, &scriptTextDesc);
767 if (err != noErr) return err;
769 /* compile the script */
770 err = OSACompile(theComponent, &scriptTextDesc,
771 kOSAModeNull, &scriptID);
773 return err;
774 }
776 /* runs the compiled applescript */
777 static void runScript()
778 {
779 /* run the script */
780 err = OSAExecute(theComponent, scriptID, kOSANullScript,
781 kOSAModeNull, &resultID);
782 return err;
783 }
786 /* Simple shortcut to the function that actually compiles the applescript. */
787 static OSStatus SimpleCompileAppleScript(const char* theScript) {
788 return CompileAppleScript(theScript, strlen(theScript), NULL);
789 }