/* Copyright (C) 2002-2003, Jeffrey D. Argast. The authors make NO WARRANTY or representation, either express or implied, with respect to this software, its quality, accuracy, merchantability, or fitness for a particular purpose. This software is provided "AS IS", and you, its user, assume the entire risk as to its quality and accuracy. Permission is hereby granted to use, copy, modify, and distribute this software or portions thereof for any purpose, without fee, subject to these conditions: (1) If any part of the source code is distributed, then this statement must be included, with this copyright and no-warranty notice unaltered. (2) Permission for use of this software is granted only if the user accepts full responsibility for any undesirable consequences; the authors accept NO LIABILITY for damages of any kind. */ #import "PerforceAction.h" #import "PerforceDepot.h" #import "PerforceDirectory.h" #import "PerforceChangeList.h" #import "PerforceChangeFile.h" #import "AppUtils.h" #import "AppDefaults.h" #import "MessageDefs.h" #import static NSString* kNewLine = @"\n"; @implementation PerforceAction + (NSTask*) runCommand: (NSArray*) commandAndArgs stdInString: (NSString*) stdInString owner: (id) owner readSelector: (SEL) readSelector readToEOF:(BOOL)readToEOF; { NSTask* perforceTask = nil; NSString* p4exe = GetPerforceExecPath(); NSMutableString* consoleMsg = [NSMutableString stringWithCString:"executing p4"]; int numArgs = [commandAndArgs count]; for ( int n = 0; n < numArgs; n++ ) { [consoleMsg appendString:@" "]; [consoleMsg appendString:[commandAndArgs objectAtIndex:n]]; } if ( stdInString ) { [consoleMsg appendString:@" "]; [consoleMsg appendString:stdInString]; } // append a new line if the last character of the console is not a newline if ( [consoleMsg compare:kNewLine options:0 range:NSMakeRange([consoleMsg length] - 1, 1)] != NSOrderedSame ) { [consoleMsg appendString:kNewLine]; } SendExecutingMessage ([consoleMsg cString]); if ( p4exe ) { NSPipe* standardOutPipe = [NSPipe pipe]; NSPipe* standardErrPipe = [NSPipe pipe]; NSPipe* standardInPipe = nil; NSFileHandle* standardIn = nil; perforceTask = [[[NSTask alloc] init] autorelease]; [perforceTask setLaunchPath:p4exe]; [perforceTask setStandardOutput:standardOutPipe]; [perforceTask setStandardError:standardErrPipe]; if ( stdInString ) { standardInPipe = [NSPipe pipe]; standardIn = [standardInPipe fileHandleForWriting]; [perforceTask setStandardInput:standardInPipe]; } [perforceTask setArguments:commandAndArgs]; // tell p4 to go do its thing [perforceTask launch]; // send it data if needed if ( standardInPipe ) { NSData* stdInData = [stdInString dataUsingEncoding:NSASCIIStringEncoding]; [standardIn writeData:stdInData]; [standardIn closeFile]; } NSFileHandle* standardOut = [standardOutPipe fileHandleForReading]; if ( readToEOF ) { [[NSNotificationCenter defaultCenter] addObserver:owner selector:readSelector name:NSFileHandleReadToEndOfFileCompletionNotification object:standardOut]; [standardOut readToEndOfFileInBackgroundAndNotify]; } else { [[NSNotificationCenter defaultCenter] addObserver:owner selector:readSelector name:NSFileHandleReadCompletionNotification object:standardOut]; [standardOut readInBackgroundAndNotify]; } } return perforceTask; } static int gNumCriticalActionsRunning = 0; + (void) pushCriticalAction { gNumCriticalActionsRunning++; } + (void) popCriticalAction { gNumCriticalActionsRunning--; } + (BOOL) isCriticalActionRunning { if ( gNumCriticalActionsRunning > 0 ) { return YES; } return NO; } + (NSArray*) runningActions { static NSMutableArray* gRunningActions = nil; if ( !gRunningActions ) { gRunningActions = [[NSMutableArray alloc] init]; } return gRunningActions; } + (void) addAction: (PerforceAction*) perforceAction { NSMutableArray* runningActions = (NSMutableArray*) [PerforceAction runningActions]; [runningActions addObject:perforceAction]; if ( [perforceAction isCriticalAction] ) { [PerforceAction pushCriticalAction]; } SendNotification (kActionListChanged); } + (void) removeAction: (PerforceAction*) perforceAction { NSMutableArray* runningActions = (NSMutableArray*) [PerforceAction runningActions]; [runningActions removeObjectIdenticalTo:perforceAction]; if ( [perforceAction isCriticalAction] ) { [PerforceAction popCriticalAction]; } SendNotification (kActionListChanged); } + (void) abortAllActions { NSArray* runningActions = [NSArray arrayWithArray:[PerforceAction runningActions]]; int n = 0; int num = [runningActions count]; for ( n = 0; n < num; n++ ) { PerforceAction* action = [runningActions objectAtIndex:n]; [action abort]; } } + (void) abortAllOwnersActions: (id) owner { NSMutableArray* runningActions = (NSMutableArray*) [PerforceAction runningActions]; NSMutableArray* toAbort = [[[NSMutableArray alloc] init] autorelease]; int n; int num = [runningActions count]; for ( n = 0; n < num; n++ ) { PerforceAction* action = [runningActions objectAtIndex:n]; if ( [action getOwner] == owner ) { [toAbort addObject:action]; } } num = [toAbort count]; for ( n = 0; n < num; n++ ) { PerforceAction* action = [toAbort objectAtIndex:n]; [action abort]; } } // Standard init method. client and user are not set - (id) init { if (self = [super init]) { fClientName = nil; fUserName = nil; } fWasError = NO; fWasAborted = NO; // NSLog (@"Task init"); // NSLog ([self description]); return self; } // Init with a special client and/or user - (id) initWithClient:(NSString*)client user:(NSString*) user; { if (self = [super init]) { if (client) { fClientName = client; [fClientName retain]; } if (user) { fUserName = user; [fUserName retain]; } } return self; } - (void) dealloc { // NSLog (@"Task dealloc"); // NSLog ([self description]); if ( fPerforceTask ) { //NSLog (@"Perforce task not yet complete. Too many release messages"); [[NSNotificationCenter defaultCenter] removeObserver:self]; [fPerforceTask terminate]; [fPerforceTask release]; } [fClientName release]; [fUserName release]; [fStandardOutput release]; [fStandardError release]; [fStandardInput release]; [fActionDescription release]; [super dealloc]; } - (void) readToEOFNotification:(NSNotification*) aNotification { NSData* data = [[aNotification userInfo] objectForKey:NSFileHandleNotificationDataItem]; fStandardOutput = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]; if ( [fStandardOutput length] > 0 ) { [self processOutput:fStandardOutput]; } [self taskDidComplete]; } - (void) readNotification:(NSNotification*) aNotification { NSData* data = [[aNotification userInfo] objectForKey:NSFileHandleNotificationDataItem]; NSString* dataString = [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease]; if ( [dataString length] > 0 ) { [self processOutput:dataString]; [[aNotification object] readInBackgroundAndNotify]; } else { [self taskDidComplete]; } } - (void) runAction:(NSArray*)commandAndArgs stdInString:(NSString*)stdInString readToEOF:(BOOL)readToEOF { [self retain]; NSMutableString* actionDesc = [[[NSMutableString alloc] init] autorelease]; [actionDesc appendString:@"p4"]; int i; int len = [commandAndArgs count]; for ( i = 0; i < len; i++ ) { [actionDesc appendString:@" "]; [actionDesc appendString:[commandAndArgs objectAtIndex:i]]; } fActionDescription = [actionDesc retain]; if ( readToEOF ) { fPerforceTask = [[PerforceAction runCommand:commandAndArgs stdInString:stdInString owner:self readSelector:@selector(readToEOFNotification:) readToEOF:readToEOF] retain]; } else { fPerforceTask = [[PerforceAction runCommand:commandAndArgs stdInString:stdInString owner:self readSelector:@selector(readNotification:) readToEOF:readToEOF] retain]; } [PerforceAction addAction:self]; if ( !fPerforceTask ) { fWasError = YES; [self taskDidComplete]; } } - (void) abort { //NSLog (@"abort called"); fWasAborted = YES; if ( fPerforceTask ) { [fPerforceTask terminate]; } [self taskDidComplete]; } - (BOOL) isCriticalAction { return NO; } - (NSString*) getActionDescription { return fActionDescription; } - (BOOL) wasError { return fWasError; } - (BOOL) wasAborted { return fWasAborted; } - (BOOL) wasSuccess { return (!fWasError && !fWasAborted); } - (NSString*) getOutput { return fStandardOutput; } - (NSString*) getError { return fStandardError; } - (NSString*) getInput { return fStandardInput; } - (id) getOwner { return fOwner; } - (void) processOutput: (NSString*) perforceOutput { SendOutputMessage ([perforceOutput cString]); } - (void) processError: (NSString*) perforceError { SendErrorMessage ([perforceError cString]); } - (void) taskDidComplete { [PerforceAction removeAction:self]; NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; [center removeObserver:self]; if ( fPerforceTask ) { if ( [fPerforceTask isRunning] ) { //NSLog (@"perforce task still running"); [fPerforceTask waitUntilExit]; } fTerminationReturnCode = [fPerforceTask terminationStatus]; //NSLog (@"termination status = %d", fTerminationReturnCode); if ( fTerminationReturnCode != 0 ) { fWasError = YES; } NSPipe* standardErr = [fPerforceTask standardError]; NSFileHandle* stdErr = [standardErr fileHandleForReading]; NSData* nsData; nsData = [stdErr readDataToEndOfFile]; fStandardError = [[NSString alloc] initWithData:nsData encoding:NSASCIIStringEncoding]; [fPerforceTask release]; fPerforceTask = nil; if ( [fStandardError length] > 0 ) { [self processError:fStandardError]; } } [self notifyOwnerTaskIsComplete]; [self autorelease]; } - (void) notifyOwnerTaskIsComplete { [fOwner performSelector:fSelector withObject:self]; } /* // // CLIENTINFO // - (PerforceClient*) clientInfo: (NSString*) clientName { ClientUserClient ui; std::vector p4args; std::string commandStr ("client"); p4args.push_back ("-o"); if (clientName) p4args.push_back ([clientName cString]); [self performCommand:commandStr withUI:&ui withArgs:p4args setProtocol:NO]; return ui.getClient(); } // // WHERE // - (NSString*) where:(NSString*) depotFilePath { ClientUserWhere ui; std::vector p4args; std::string commandStr ("where"); p4args.push_back ([depotFilePath cString]); [self performCommand:commandStr withUI:&ui withArgs:p4args setProtocol:NO]; return ui.getClientPath(); } */ @end