PerforceAction.mm #5

  • //
  • guest/
  • jeff_argast/
  • P4Cocoa/
  • source/
  • Perforce/
  • PerforceAction.mm
  • View
  • Commits
  • Open Download .zip Download (11 KB)
/*

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 <unistd.h>

static NSString* kNewLine = @"\n";

@implementation PerforceAction

+ (NSTask*) runCommand: (NSArray*) commandAndArgs
			stdInString: (NSString*) stdInString
			owner: (id) owner
			readSelector: (SEL) readSelector
			readToEOF:(BOOL)readToEOF
			sendStdInToConsole:(BOOL)sendStdInToConsole;
{
	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 && sendStdInToConsole )
	{
		[consoleMsg appendString:@" "];
		
		[consoleMsg appendString:stdInString];
	}
	
	// append a new line if the last character of the console is not a newline
	// compare:kNewLine options:0 range:NSMakeRange([consoleMsg length] - 1, 1)] != NSOrderedSame )
	if ( [consoleMsg characterAtIndex:[consoleMsg length] - 1] != '\n' )
	{
		[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];
			
			// using standard in is slow when writing a large amount of data
			[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 %@", [self description]);
//	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 %@", [self description]);
//	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 runAction:commandAndArgs stdInString:stdInString readToEOF:readToEOF sendStdInToConsole:YES];
}

- (void) runAction:(NSArray*)commandAndArgs stdInString:(NSString*)stdInString readToEOF:(BOOL)readToEOF sendStdInToConsole:(BOOL)sendStdInToConsole
{
	[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
									sendStdInToConsole:sendStdInToConsole] retain];
	}
	else
	{
		fPerforceTask = [[PerforceAction runCommand:commandAndArgs 
									stdInString:stdInString 
									owner:self 
									readSelector:@selector(readNotification:)
									readToEOF:readToEOF
									sendStdInToConsole:sendStdInToConsole] 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<std::string> 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<std::string> p4args;

    std::string commandStr ("where");
    p4args.push_back ([depotFilePath cString]);

    [self performCommand:commandStr withUI:&ui withArgs:p4args setProtocol:NO];

    return ui.getClientPath();
}
*/

@end

# Change User Description Committed
#6 3931 Jeff Argast Fixed a hang when actin on large numbers of files
Added an icon for xcode
Switched to xcode
Tweaked the icons in the tabs for panther
#5 3147 Jeff Argast Testing std in blockage
#4 3130 Jeff Argast Added double click support to the depot view and pending changelist
view.  Added View File In Editor item on the pending changeist
context menu.
#3 3113 Jeff Argast Reduced the times the depot view completely collapses.
Now it won't collapse on refresh views or submit, but
still collapses when the defaults change.
#2 3111 Jeff Argast Made multiple selection smarter by operating on the entire selection as an atomic
operation with the server. Also partially fixed the read only window to not wrap
at the window boundary.  I did the same for the editable window, but now the problem
appears to be that p4 change -o is breaking its output at some character location
before the string gets into the editor (at least I think that is the problem).
#1 2732 Jeff Argast Initial submission of P4Cocoa