Code

- Put the fontconfig test flag for the Mac package in the ~/.inkscape-etc/
[inkscape.git] / packaging / macosx / ScriptExec / main.c
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
9  
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[])
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);
162     
163     err += AEInstallEventHandler(kCoreEventClass, kAEReopenApplication,
164                                  NewAEEventHandlerUPP(AppReopenAppAEHandler),
165                                  0, false);
166     
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.");
180     
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");
185     
186     RunApplicationEventLoop(); //Run the event loop
187     return 0;
189                                  
190 #pragma mark -
193 static void RequestUserAttention(void)
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));
209 static void ShowFirstStartWarningDialog(void)
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             &params, &itemHit);
230 //////////////////////////////////
231 // Handler for when fontconfig caches need to be generated
232 //////////////////////////////////
233 static OSStatus FCCacheFailedHandler(EventHandlerCallRef theHandlerCall, 
234                                  EventRef theEvent, void *userData)
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;
256 static size_t safeRead(int d, void *buf, size_t nbytes)
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;
289 /////////////////////////////////////
290 // Code to run fc-cache on first run
291 /////////////////////////////////////
292 static OSStatus FixFCCache (void)
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);
340                         
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;
366 ///////////////////////////////////
367 // Execution thread starts here
368 ///////////////////////////////////
369 static void *Execute (void *arg)
371     EventRef event;
372     
373     taskDone = false;
374     
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;
390 ///////////////////////////////////
391 // Open additional documents thread starts here
392 ///////////////////////////////////
393 static void *OpenDoc (void *arg)
395     ExecuteScript(openDocPath, NULL);
396     return 0;
399 ///////////////////////////////////////
400 // Run a script via the system command
401 ///////////////////////////////////////
402 static OSErr ExecuteScript (char *script, pid_t *pid)
404     pid_t wpid = 0, p = 0;
405     int status, i;
406  
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
415     
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
423     
424     if (wpid == (pid_t)-1) return wpid;
425     return (OSErr)WEXITSTATUS(status);
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)
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);
441     
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);
448 ///////////////////////////////////////
449 // Get path to the script in Resources folder
450 ///////////////////////////////////////
451 static char* GetScript (void)
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;
467     
468     //Get file reference from Core Foundation URL
469     if (! CFURLGetFSRef(scriptFileURL, &fileRef)) return NULL;
470     
471     //dispose of the CF variables
472     CFRelease(scriptFileURL);
473     CFRelease(fileName);
474     
475     //convert FSRef to FSSpec
476     if (FSGetCatalogInfo(&fileRef, kFSCatInfoNone, NULL, NULL, &fileSpec,
477                          NULL)) return NULL;
478         
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;
483     
484     return path;
487 ///////////////////////////////////////
488 // Gets the path to openDoc in Resources folder
489 ///////////////////////////////////////
490 static char* GetOpenDoc (void)
492     CFStringRef fileName;
493     CFBundleRef appBundle;
494     CFURLRef openDocFileURL;
495     FSRef fileRef;
496     FSSpec fileSpec;
497     char *path;
498     
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;
506     
507     //Get file reference from Core Foundation URL
508     if (! CFURLGetFSRef( openDocFileURL, &fileRef )) return NULL;
509     
510     //dispose of the CF variables
511     CFRelease(openDocFileURL);
512     CFRelease(fileName);
513         
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;
522     
523     return path;
526 #pragma mark -
528 /////////////////////////////////////
529 // Load menu bar from nib
530 /////////////////////////////////////
531 OSErr LoadMenuBar (char *appName)
533     OSErr err;
534     IBNibRef nibRef;
535     
536     if (err = CreateNibReference(CFSTR("MenuBar"), &nibRef)) return err;
537     if (err = SetMenuBarFromNib(nibRef, CFSTR("MenuBar"))) return err;
538     DisposeNibReference(nibRef);
540     return noErr;
543 #pragma mark -
545 ///////////////////////////////////////
546 // Generate path string from FSSpec record
547 ///////////////////////////////////////
548 static OSStatus FSMakePath(FSSpec file, char *path, long maxPathSize)
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);
560 ////////////////////////////////////////
561 // Standard red error alert, then exit application
562 ////////////////////////////////////////
563 static void RedFatalAlert (Str255 errorString, Str255 expStr)
565     StandardAlert(kAlertStopAlert, errorString,  expStr, NULL, NULL);
566     ExitToShell();
569 ///////////////////////////////////////
570 // Determines whether file exists at path or not
571 ///////////////////////////////////////
572 static short DoesFileExist (char *path)
574     if (access(path, F_OK) == -1) return false;
575     return true;        
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)
587     #pragma unused (reply, refCon, theAppleEvent)
589     while (numArgs > 0) free(fileArgs[numArgs--]);
590     
591     if (! taskDone && pid) { //kill the script process brutally
592         kill(pid, 9);
593         printf("Platypus App: PID %d killed brutally\n", pid);
594     }
595     
596     pthread_cancel(tid);
597     if (odtid) pthread_cancel(odtid);
598     
599     ExitToShell();
600     
601     return noErr;
604 /////////////////////////////////////
605 // Handler for docs dragged on app icon
606 /////////////////////////////////////
607 static OSErr AppOpenDocAEHandler(const AppleEvent *theAppleEvent,
608                                  AppleEvent *reply, long refCon)
610     #pragma unused (reply, refCon)
611         
612     OSErr err = noErr;
613     AEDescList fileSpecList;
614     AEKeyword keyword;
615     DescType type;
616         
617     short i;
618     long count, actualSize;
619         
620     FSSpec fileSpec;
621     char path[kMaxPathLength];
622     
623     while (numArgs > 0) free(fileArgs[numArgs--]);
624         
625     //Read the AppleEvent
626     err = AEGetParamDesc(theAppleEvent, keyDirectObject, typeAEList,
627                          &fileSpecList);
628                 
629     err = AECountItems(&fileSpecList, &count); //Count number of files
630                 
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;
639                             
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     }
648         
649     if (! taskDone) pthread_create(&odtid, NULL, OpenDoc, NULL);
650     else pthread_create(&tid, NULL, Execute, NULL);
651         
652     return err;
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)
662     runScript();
665 // if app is being opened
666 static OSErr AppOpenAppAEHandler(const AppleEvent *theAppleEvent,
667                                  AppleEvent *reply, long refCon)
669     #pragma unused (reply, refCon, theAppleEvent)
670         
671     // the app has been opened without any items dragged on to it
672     pthread_create(&tid, NULL, Execute, NULL);
674     return noErr;
678 static void OpenURL(Str255 url)
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         }
701 //////////////////////////////////
702 // Handler for when X11 fails to start
703 //////////////////////////////////
704 static OSStatus X11FailedHandler(EventHandlerCallRef theHandlerCall, 
705                                  EventRef theEvent, void *userData)
707     #pragma unused(theHanderCall, theEvent, userData)
709     pthread_join(tid, NULL);
710     if (odtid) pthread_join(odtid, NULL);
711  
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                         &params, &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;
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) {
752     
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;
759     
760     /* open the scripting component */
761     theComponent = OpenDefaultComponent(kOSAComponentType,
762                                         typeAppleScript);
763     if (theComponent == NULL) { err = paramErr; return err; }
764     
765     /* put the script text into an aedesc */
766     err = AECreateDesc(typeChar, text, textLength, &scriptTextDesc);
767     if (err != noErr) return err;
768     
769     /* compile the script */
770     err = OSACompile(theComponent, &scriptTextDesc,
771                      kOSAModeNull, &scriptID);
773     return err;
776 /* runs the compiled applescript */
777 static void runScript()
779     /* run the script */
780     err = OSAExecute(theComponent, scriptID, kOSANullScript,
781                      kOSAModeNull, &resultID);
782     return err;
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);