// // FSAApp.m // F-Script Anywhere // // Created by Nicholas Riley on Fri Feb 01 2002. // Copyright (c) 2002 Nicholas Riley. All rights reserved. // /* F-Script Anywhere is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. F-Script Anywhere is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with F-Script Anywhere; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #import #import "FSAApp.h" #import "FSAnywhere.h" NSString * const PatchBundleIdentifier = @"net.sabi.FScriptAnywhere"; typedef struct { OSStatus status; NSString * const desc; } errRec, errList[]; // These are just educated guesses based on the errors I've seen; I haven't seen these documented anywhere! static errList ERRS = { { 5, @"F-Script Anywhere must be installed in a Cocoa application running as the current user.\n\nYou may be attempting to install in a setuid application, which is not supported" }, { 11, @"F-Script Anywhere cannot be installed in itself.\n\nIf you wish to install F-Script Anywhere in itself, make a copy of the F-Script Anywhere application and install one copy into the other" }, { smUnExBusErr, @"a bus error occurred.\n\nTry switching to the application first then using F-Script AnywhereÕs dock menu to install" }, { fnfErr, @"F-Script Anywhere was unable to locate its component to install in the application. Please try reinstalling F-Script Anywhere" }, { cfragDupRegistrationErr, @"another running copy of F-Script Anywhere is already installed in the application"}, { 0, nil } }; NSMutableDictionary *FSA_errors; NSString * FSA_descriptionForOSStatus(OSStatus status) { NSString *desc = nil; if (FSA_errors != nil) desc = [FSA_errors objectForKey: [NSNumber numberWithLong: status]]; if (desc != nil) return desc; return [NSString stringWithFormat: @"an error of type %ld occurred", status]; } @implementation FSAApp + (void)initialize; { errRec *rec; FSA_errors = [[NSMutableDictionary alloc] init]; for (rec = &(ERRS[0]) ; rec->status != 0 ; rec++) [FSA_errors setObject: rec->desc forKey: [NSNumber numberWithLong: rec->status]]; } - (void)finishLaunching { // yes, someone could move the framework while the app is running, but they deserve what they get. NSFileManager *fileMgr = [NSFileManager defaultManager]; NSArray *libraryDirectories = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask, TRUE); NSEnumerator *e = [libraryDirectories objectEnumerator]; NSString *libPath; NSString *frameworkPath; BOOL isDirectory; BOOL found = NO; while ( (libPath = [e nextObject]) != nil) { frameworkPath = [libPath stringByAppendingPathComponent: @"Frameworks"]; if ([fileMgr fileExistsAtPath: [frameworkPath stringByAppendingPathComponent: @"FScript.framework"] isDirectory: &isDirectory] && isDirectory) { found = YES; } } [super finishLaunching]; if (!found) { int result = NSRunInformationalAlertPanel(@"F-Script Framework Not Found", @"F-Script Anywhere requires the F-Script framework be installed in a Frameworks directory, such as ~/Library/Frameworks or /Library/Frameworks.\n\nTo download F-Script, please visit its Web site %@.\n\nIf you believe this message is in error, click ÒContinueÓ.", @"Quit", @"Get F-Script", @"Continue", FSA_FScriptURL); switch (result) { case NSAlertOtherReturn: break; case NSAlertAlternateReturn: [[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString: FSA_FScriptURL]]; case NSAlertDefaultReturn: default: [self terminate: self]; return; } } [[PatchController sharedPatchController] setDelegate: self]; // In Jaguar 6C115, libPatch no longer delivers process death notifications to the application unless it registers for NSWorkspaceDidTerminateApplicationNotification. (Process launch notifications are still received.) [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self selector: @selector(applicationDidTerminate:) name: NSWorkspaceDidTerminateApplicationNotification object: nil]; } // This method causes the selected applications to quit, because the Objective-C runtime in Mac OS X does not support unloading of classes. - (void)unloadBundles:(id)sender { UInt32 index,count; NSArray *pids; pids = [[PatchController sharedPatchController] processPIDsForPatchWithIdentifier:PatchBundleIdentifier]; count = [pids count]; for (index = 0 ; index < count ; index ++) { [[PatchController sharedPatchController] messagePatchForProcessWithPID:[[pids objectAtIndex:index] longValue] identifier:PatchBundleIdentifier message:'bye ' data:NULL length:0]; [[PatchController sharedPatchController] unloadPatchForProcessWithPID:[[pids objectAtIndex:index] longValue] identifier:PatchBundleIdentifier]; } } - (void)installBundleInAppWithPID:(pid_t)pid; { OSStatus err; if (pid == -1) return; // XXX Switch to application first, resolves crash in Project Builder. // XXX Fix window closing problem. err = [[PatchController sharedPatchController] loadPatchForProcessWithPID:pid identifier:PatchBundleIdentifier path:[NSString stringWithFormat:@"%@/F-Script Anywhere", [[NSBundle mainBundle] resourcePath]] detached:NO]; if (err == noErr) { [[PatchController sharedPatchController] messagePatchForProcessWithPID:pid identifier:PatchBundleIdentifier message:'helo' data:NULL length:0]; [appList didPatchProcessID: pid]; } else { NSBeginAlertSheet(@"Installation failed", @"OK", nil, nil, appListPanel, self, nil, nil, nil, @"F-Script Anywhere was unable to install itself in the selected application (process ID %d), because %@.\n\nThe application may have crashed; restart it if needed.", pid, FSA_descriptionForOSStatus(err)); } } - (IBAction)installBundleInSelectedApp:(id)sender; { [self installBundleInAppWithPID: [appList selectedProcessID]]; } // from dock menu only! - (IBAction)installBundleInFrontmostApp:(id)sender; { NSAssert1([sender tag] > 0, @"Unable to determine frontmost application from %@", sender); [self installBundleInAppWithPID: (pid_t)[sender tag]]; } @end @implementation FSAApp (NSWorkspaceNotifications) - (void)applicationDidTerminate:(NSNotification *)notification; { NSDictionary *userInfo = [notification userInfo]; FSALog(@"Process terminated (pid %6d): %@\n", [[userInfo objectForKey: @"NSApplicationProcessIdentifier"] intValue], [userInfo objectForKey: @"NSApplicationName"]); // Don't do anything here, this is a dummy method triggered by a NSWorkspaceDidTerminateApplicationNotification } @end @implementation FSAApp (PatchControllerDelegate) - (void) notifyProcessLaunch:(pid_t)pid info:(NSDictionary*)info { [appList applicationLaunchedWithProcessID: pid]; } // this delegate method is never invoked in Mac OS X 10.2 unless I register for NSWorkspaceDidTerminateApplicationNotification - (void) notifyProcessDeath:(pid_t)pid info:(NSDictionary*)info { FSALog(@"Process exited (pid %6d): %@\n", pid, [info objectForKey: PatchControllerNameKey]); [appList applicationQuitWithProcessID: pid]; } @end