static void ParseDiffResults(const FP4RecordSet& InRecords, TArray<FString>& OutModifiedFiles)
{
	if (InRecords.Num() > 0)
	{
		// Iterate over each record found as a result of the command, parsing it for relevant information
		for (int32 Index = 0; Index < InRecords.Num(); ++Index)
		{
			const FP4Record& ClientRecord = InRecords[Index];
			FString FileName = ClientRecord(TEXT("clientFile"));
			FPaths::NormalizeFilename(FileName);
			OutModifiedFiles.Add(FileName);	
		}
	}
}
static void ParseFilesResults(const FP4RecordSet& InRecords, TArray< TSharedRef<ISourceControlRevision, ESPMode::ThreadSafe> >& OutRevisions, const FString& InClientRoot)
{
	// Iterate over each record found as a result of the command, parsing it for relevant information
	for (int32 Index = 0; Index < InRecords.Num(); ++Index)
	{
		const FP4Record& ClientRecord = InRecords[Index];
		FString DepotFile = ClientRecord(TEXT("depotFile"));
		FString RevisionNumber = ClientRecord(TEXT("rev"));
		FString Date = ClientRecord(TEXT("time"));
		FString ChangelistNumber = ClientRecord(TEXT("change"));
		FString Action = ClientRecord(TEXT("action"));
		check(RevisionNumber.Len() != 0);

		// @todo: this revision is incomplete, but is sufficient for now given the usage of labels to get files, rather
		// than review revision histories.
		TSharedRef<FPerforceSourceControlRevision, ESPMode::ThreadSafe> Revision = MakeShareable( new FPerforceSourceControlRevision() );
		Revision->FileName = DepotFile;
		Revision->RevisionNumber = FCString::Atoi(*RevisionNumber);
		Revision->ChangelistNumber = FCString::Atoi(*ChangelistNumber);
		Revision->Action = Action;
		Revision->Date = FDateTime(1970, 1, 1, 0, 0, 0, 0) + FTimespan(0, 0, FCString::Atoi(*Date));

		OutRevisions.Add(Revision);
	}
}
/** Simple parsing of a record set to update state */
static void ParseRecordSetForState(const FP4RecordSet& InRecords, TMap<FString, EPerforceState::Type>& OutResults)
{
	// Iterate over each record found as a result of the command, parsing it for relevant information
	for (int32 Index = 0; Index < InRecords.Num(); ++Index)
	{
		const FP4Record& ClientRecord = InRecords[Index];
		FString FileName = ClientRecord(TEXT("clientFile"));
		FString Action = ClientRecord(TEXT("action"));

		check(FileName.Len());
		FString FullPath(FileName);
		FPaths::NormalizeFilename(FullPath);

		if(Action.Len() > 0)
		{
			if(Action == TEXT("add"))
			{
				OutResults.Add(FullPath, EPerforceState::OpenForAdd);
			}
			else if(Action == TEXT("edit"))
			{
				OutResults.Add(FullPath, EPerforceState::CheckedOut);
			}
			else if(Action == TEXT("delete"))
			{
				OutResults.Add(FullPath, EPerforceState::MarkedForDelete);
			}
			else if(Action == TEXT("abandoned"))
			{
				OutResults.Add(FullPath, EPerforceState::NotInDepot);
			}
			else if(Action == TEXT("reverted"))
			{
				FString OldAction = ClientRecord(TEXT("oldAction"));
				if(OldAction == TEXT("add"))
				{
					OutResults.Add(FullPath, EPerforceState::NotInDepot);
				}
				else if(OldAction == TEXT("edit"))
				{
					OutResults.Add(FullPath, EPerforceState::ReadOnly);
				}
				else if(OldAction == TEXT("delete"))
				{
					OutResults.Add(FullPath, EPerforceState::ReadOnly);
				}
			}
			else if(Action == TEXT("branch"))
			{
				OutResults.Add(FullPath, EPerforceState::Branched);
			}
		}
	}
}
コード例 #4
0
static bool CheckUnicodeStatus(ClientApi& P4Client, bool& bIsUnicodeServer, TArray<FText>& OutErrorMessages)
{
#if USE_P4_API
	FP4RecordSet Records;
	FP4ClientUser User(Records, false, OutErrorMessages);

	P4Client.Run("info", &User);

	if(Records.Num() > 0)
	{
		bIsUnicodeServer = Records[0].Find(TEXT("unicode")) != NULL;
	}
	else
	{
		bIsUnicodeServer = false;
	}
#endif

	return OutErrorMessages.Num() == 0;
}
static void ParseOpenedResults(const FP4RecordSet& InRecords, const FString& ClientName, const FString& ClientRoot, TMap<FString, EPerforceState::Type>& OutResults)
{
	// Iterate over each record found as a result of the command, parsing it for relevant information
	for (int32 Index = 0; Index < InRecords.Num(); ++Index)
	{
		const FP4Record& ClientRecord = InRecords[Index];
		FString ClientFileName = ClientRecord(TEXT("clientFile"));
		FString Action = ClientRecord(TEXT("action"));

		check(ClientFileName.Len() > 0);

		// Convert the depot file name to a local file name
		FString FullPath = ClientFileName;
		const FString PathRoot = FString::Printf(TEXT("//%s"), *ClientName);

		if (FullPath.StartsWith(PathRoot))
		{
			const bool bIsNullClientRootPath = (ClientRoot == TEXT("null"));
			if ( bIsNullClientRootPath )
			{
				// Null clients use the pattern in PathRoot: //Workspace/FileName
				// Here we chop off the '//Workspace/' to return the workspace filename
				FullPath = FullPath.RightChop(PathRoot.Len() + 1);
			}
			else
			{
				// This is a normal workspace where we can simply replace the pathroot with the client root to form the filename
				FullPath = FullPath.Replace(*PathRoot, *ClientRoot);
			}
		}
		else
		{
			// This file is not in the workspace, ignore it
			continue;
		}

		if (Action.Len() > 0)
		{
			if(Action == TEXT("add"))
			{
				OutResults.Add(FullPath, EPerforceState::OpenForAdd);
			}
			else if(Action == TEXT("edit"))
			{
				OutResults.Add(FullPath, EPerforceState::CheckedOut);
			}
			else if(Action == TEXT("delete"))
			{
				OutResults.Add(FullPath, EPerforceState::MarkedForDelete);
			}
		}
	}
}
bool FPerforceConnectWorker::Execute(FPerforceSourceControlCommand& InCommand)
{
	FScopedPerforceConnection ScopedConnection(InCommand);
	if (!InCommand.IsCanceled() && ScopedConnection.IsValid())
	{
		FPerforceConnection& Connection = ScopedConnection.GetConnection();
		TArray<FString> Parameters;
		FP4RecordSet Records;
		Parameters.Add(TEXT("-o"));
		Parameters.Add(InCommand.ConnectionInfo.Workspace);
		InCommand.bCommandSuccessful = Connection.RunCommand(TEXT("client"), Parameters, Records, InCommand.ErrorMessages, FOnIsCancelled::CreateRaw(&InCommand, &FPerforceSourceControlCommand::IsCanceled), InCommand.bConnectionDropped);
		
		// If there are error messages, user name is most likely invalid. Otherwise, make sure workspace actually
		// exists on server by checking if we have it's update date.
		InCommand.bCommandSuccessful &= InCommand.ErrorMessages.Num() == 0 && Records.Num() > 0 && Records[0].Contains(TEXT("Update"));
		if (!InCommand.bCommandSuccessful && InCommand.ErrorMessages.Num() == 0)
		{
			InCommand.ErrorMessages.Add(LOCTEXT("InvalidWorkspace", "Invalid workspace."));
		}

		// check if we can actually work with this workspace
		if(InCommand.bCommandSuccessful)
		{
			FText Notification;
			InCommand.bCommandSuccessful = CheckWorkspaceRecordSet(Records, InCommand.ErrorMessages, Notification);
			if(!InCommand.bCommandSuccessful)
			{
				check(InCommand.Operation->GetName() == GetName());
				TSharedRef<FConnect, ESPMode::ThreadSafe> Operation = StaticCastSharedRef<FConnect>(InCommand.Operation);
				Operation->SetErrorText(Notification);
			}
		}

		if(InCommand.bCommandSuccessful)
		{
			ParseRecordSet(Records, InCommand.InfoMessages);
		}
	}
	return InCommand.bCommandSuccessful;
}
/** Simple parsing of a record set into strings, one string per record */
static void ParseRecordSet(const FP4RecordSet& InRecords, TArray<FText>& OutResults)
{
	const FString Delimiter = FString(TEXT(" "));

	for (int32 RecordIndex = 0; RecordIndex < InRecords.Num(); ++RecordIndex)
	{
		const FP4Record& ClientRecord = InRecords[RecordIndex];
		for(FP4Record::TConstIterator It = ClientRecord.CreateConstIterator(); It; ++It)
		{
			OutResults.Add(FText::FromString(It.Key() + Delimiter + It.Value()));
		}
	}
}
static void ParseGetLabelsResults(const FP4RecordSet& InRecords, TArray< TSharedRef<ISourceControlLabel> >& OutLabels)
{
	// Iterate over each record found as a result of the command, parsing it for relevant information
	for (int32 Index = 0; Index < InRecords.Num(); ++Index)
	{
		const FP4Record& ClientRecord = InRecords[Index];
		FString LabelName = ClientRecord(TEXT("label"));
		if(LabelName.Len() > 0)
		{
			OutLabels.Add(MakeShareable( new FPerforceSourceControlLabel(LabelName) ) );
		}
	}
}
コード例 #9
0
/**
 * Runs "client" command to test if the connection is actually OK. ClientApi::Init() only checks
 * if it can connect to server, doesn't verify user name nor workspace name.
 */
static bool TestConnection(ClientApi& P4Client, const FString& ClientSpecName, bool bIsUnicodeServer, TArray<FText>& OutErrorMessages)
{
	FP4RecordSet Records;
	FP4ClientUser User(Records, bIsUnicodeServer, OutErrorMessages);

	UTF8CHAR* ClientSpecUTF8Name = nullptr;
	if(bIsUnicodeServer)
	{
		FTCHARToUTF8 UTF8String(*ClientSpecName);
		ClientSpecUTF8Name = new UTF8CHAR[UTF8String.Length() + 1];
		FMemory::Memcpy(ClientSpecUTF8Name, UTF8String.Get(), UTF8String.Length() + 1);
	}
	else
	{		
		ClientSpecUTF8Name = new UTF8CHAR[ClientSpecName.Len() + 1];
		FMemory::Memcpy(ClientSpecUTF8Name, TCHAR_TO_ANSI(*ClientSpecName), ClientSpecName.Len() + 1);
	}

	const char *ArgV[] = { "-o", (char*)ClientSpecUTF8Name };
#if USE_P4_API
	P4Client.SetArgv(2, const_cast<char*const*>(ArgV));

	P4Client.Run("client", &User);
#endif

	// clean up args
	delete [] ClientSpecUTF8Name;

	// If there are error messages, user name is most likely invalid. Otherwise, make sure workspace actually
	// exists on server by checking if we have it's update date.
	bool bConnectionOK = OutErrorMessages.Num() == 0 && Records.Num() > 0 && Records[0].Contains(TEXT("Update"));
	if (!bConnectionOK && OutErrorMessages.Num() == 0)
	{
		OutErrorMessages.Add(LOCTEXT("InvalidWorkspace", "Invalid Workspace"));
	}

	return bConnectionOK;
}
static FText ParseSubmitResults(const FP4RecordSet& InRecords)
{
	// Iterate over each record found as a result of the command, parsing it for relevant information
	for (int32 Index = 0; Index < InRecords.Num(); ++Index)
	{
		const FP4Record& ClientRecord = InRecords[Index];
		const FString SubmittedChange = ClientRecord(TEXT("submittedChange"));
		if(SubmittedChange.Len() > 0)
		{
			return FText::Format(LOCTEXT("SubmitMessage", "Submitted changelist {0}"), FText::FromString(SubmittedChange));
		}
	}

	return LOCTEXT("SubmitMessageUnknown", "Submitted changelist");
}
static void ParseSyncResults(const FP4RecordSet& InRecords, TMap<FString, EPerforceState::Type>& OutResults)
{
	// Iterate over each record found as a result of the command, parsing it for relevant information
	for (int32 Index = 0; Index < InRecords.Num(); ++Index)
	{
		const FP4Record& ClientRecord = InRecords[Index];
		FString FileName = ClientRecord(TEXT("clientFile"));
		FString Action = ClientRecord(TEXT("action"));

		check(FileName.Len());
		FString FullPath(FileName);
		FPaths::NormalizeFilename(FullPath);

		if(Action.Len() > 0)
		{
			if(Action == TEXT("updated"))
			{
				OutResults.Add(FullPath, EPerforceState::ReadOnly);
			}
		}
	}
}
static void ParseHistoryResults(const FP4RecordSet& InRecords, const TArray<FPerforceSourceControlState>& InStates, FPerforceUpdateStatusWorker::FHistoryMap& OutHistory)
{
	if (InRecords.Num() > 0)
	{
		// Iterate over each record, extracting the relevant information for each
		for (int32 RecordIndex = 0; RecordIndex < InRecords.Num(); ++RecordIndex)
		{
			const FP4Record& ClientRecord = InRecords[RecordIndex];

			// Extract the file name 
			check(ClientRecord.Contains(TEXT("depotFile")));
			FString DepotFileName = ClientRecord(TEXT("depotFile"));
			FString LocalFileName = FindWorkspaceFile(InStates, DepotFileName);

			TArray< TSharedRef<FPerforceSourceControlRevision, ESPMode::ThreadSafe> > Revisions;
			int32 RevisionNumbers = 0;
			for (;;)
			{
				// Extract the revision number
				FString VarName = FString::Printf(TEXT("rev%d"), RevisionNumbers);
				if (!ClientRecord.Contains(*VarName))
				{
					// No more revisions
					break;
				}
				FString RevisionNumber = ClientRecord(*VarName);

				// Extract the user name
				VarName = FString::Printf(TEXT("user%d"), RevisionNumbers);
				check(ClientRecord.Contains(*VarName));
				FString UserName = ClientRecord(*VarName);

				// Extract the date
				VarName = FString::Printf(TEXT("time%d"), RevisionNumbers);
				check(ClientRecord.Contains(*VarName));
				FString Date = ClientRecord(*VarName);

				// Extract the changelist number
				VarName = FString::Printf(TEXT("change%d"), RevisionNumbers);
				check(ClientRecord.Contains(*VarName));
				FString ChangelistNumber = ClientRecord(*VarName);

				// Extract the description
				VarName = FString::Printf(TEXT("desc%d"), RevisionNumbers);
				check(ClientRecord.Contains(*VarName));
				FString Description = ClientRecord(*VarName);

				// Extract the action
				VarName = FString::Printf(TEXT("action%d"), RevisionNumbers);
				check(ClientRecord.Contains(*VarName));
				FString Action = ClientRecord(*VarName);

				FString FileSize(TEXT("0"));

				// Extract the file size
				if(Action.ToLower() != TEXT("delete") && Action.ToLower() != TEXT("move/delete")) //delete actions don't have a fileSize from PV4
				{
					VarName = FString::Printf(TEXT("fileSize%d"), RevisionNumbers);
					check(ClientRecord.Contains(*VarName));
					FileSize = ClientRecord(*VarName);
				}
		
				// Extract the clientspec/workspace
				VarName = FString::Printf(TEXT("client%d"), RevisionNumbers);
				check(ClientRecord.Contains(*VarName));
				FString ClientSpec = ClientRecord(*VarName);

				// check for branch
				TSharedPtr<FPerforceSourceControlRevision, ESPMode::ThreadSafe> BranchSource;
				VarName = FString::Printf(TEXT("how%d,0"), RevisionNumbers);
				if(ClientRecord.Contains(*VarName))
				{
					BranchSource = MakeShareable( new FPerforceSourceControlRevision() );

					VarName = FString::Printf(TEXT("file%d,0"), RevisionNumbers);
					FString BranchSourceFileName = ClientRecord(*VarName);
					BranchSource->FileName = FindWorkspaceFile(InStates, BranchSourceFileName);

					VarName = FString::Printf(TEXT("erev%d,0"), RevisionNumbers);
					FString BranchSourceRevision = ClientRecord(*VarName);
					BranchSource->RevisionNumber = FCString::Atoi(*BranchSourceRevision);
				}

				TSharedRef<FPerforceSourceControlRevision, ESPMode::ThreadSafe> Revision = MakeShareable( new FPerforceSourceControlRevision() );
				Revision->FileName = LocalFileName;
				Revision->RevisionNumber = FCString::Atoi(*RevisionNumber);
				Revision->Revision = RevisionNumber;
				Revision->ChangelistNumber = FCString::Atoi(*ChangelistNumber);
				Revision->Description = Description;
				Revision->UserName = UserName;
				Revision->ClientSpec = ClientSpec;
				Revision->Action = Action;
				Revision->BranchSource = BranchSource;
				Revision->Date = FDateTime(1970, 1, 1, 0, 0, 0, 0) + FTimespan::FromSeconds(FCString::Atoi(*Date));
				Revision->FileSize = FCString::Atoi(*FileSize);

				Revisions.Add(Revision);

				RevisionNumbers++;
			}

			if(Revisions.Num() > 0)
			{
				OutHistory.Add(LocalFileName, Revisions);
			}
		}
	}
}
static void ParseUpdateStatusResults(const FP4RecordSet& InRecords, const TArray<FText>& ErrorMessages, TArray<FPerforceSourceControlState>& OutStates)
{
	// Iterate over each record found as a result of the command, parsing it for relevant information
	for (int32 Index = 0; Index < InRecords.Num(); ++Index)
	{
		const FP4Record& ClientRecord = InRecords[Index];
		FString FileName = ClientRecord(TEXT("clientFile"));
		FString DepotFileName = ClientRecord(TEXT("depotFile"));
		FString HeadRev  = ClientRecord(TEXT("headRev"));
		FString HaveRev  = ClientRecord(TEXT("haveRev"));
		FString OtherOpen = ClientRecord(TEXT("otherOpen"));
		FString OpenType = ClientRecord(TEXT("type"));
		FString HeadAction = ClientRecord(TEXT("headAction"));
		FString Action = ClientRecord(TEXT("action"));
		FString HeadType = ClientRecord(TEXT("headType"));
		const bool bUnresolved = ClientRecord.Contains(TEXT("unresolved"));

		FString FullPath(FileName);
		FPaths::NormalizeFilename(FullPath);
		OutStates.Add(FPerforceSourceControlState(FullPath));
		FPerforceSourceControlState& State = OutStates.Last();
		State.DepotFilename = DepotFileName;

		State.State = EPerforceState::ReadOnly;
		if (Action.Len() > 0 && Action == TEXT("add")) 
		{
			State.State = EPerforceState::OpenForAdd;
		}
		else if (Action.Len() > 0 && Action == TEXT("delete")) 
		{
			State.State = EPerforceState::MarkedForDelete;
		}
		else if (OpenType.Len() > 0)
		{
			if(Action.Len() > 0 && Action == TEXT("branch")) 
			{
				State.State = EPerforceState::Branched;
			}
			else
			{
				State.State = EPerforceState::CheckedOut;
			}
		}
		else if (OtherOpen.Len() > 0)
		{
			// OtherOpen just reports the number of developers that have a file open, now add a string for every entry
			int32 OtherOpenNum = FCString::Atoi(*OtherOpen);
			for ( int32 OpenIdx = 0; OpenIdx < OtherOpenNum; ++OpenIdx )
			{
				const FString OtherOpenRecordKey = FString::Printf(TEXT("otherOpen%d"), OpenIdx);
				const FString OtherOpenRecordValue = ClientRecord(OtherOpenRecordKey);

				State.OtherUserCheckedOut += OtherOpenRecordValue;
				if(OpenIdx < OtherOpenNum - 1)
				{
					State.OtherUserCheckedOut += TEXT(", ");
				}
			}

			State.State = EPerforceState::CheckedOutOther;
		}
		//file has been previously deleted, ok to add again
		else if (HeadAction.Len() > 0 && HeadAction == TEXT("delete")) 
		{
			State.State = EPerforceState::NotInDepot;
		}
		
		if (HeadRev.Len() > 0 && HaveRev.Len() > 0)
		{
			TTypeFromString<int>::FromString(State.DepotRevNumber, *HeadRev);
			TTypeFromString<int>::FromString(State.LocalRevNumber, *HaveRev);
			if( bUnresolved )
			{
				int32 ResolveActionNumber = 0;
				for (;;)
				{
					// Extract the revision number
					FString VarName = FString::Printf(TEXT("resolveAction%d"), ResolveActionNumber);
					if (!ClientRecord.Contains(*VarName))
					{
						// No more revisions
						ensureMsgf( ResolveActionNumber > 0, TEXT("Resolve is pending but no resolve actions for file %s"), *FileName );
						break;
					}

					VarName = FString::Printf(TEXT("resolveBaseFile%d"), ResolveActionNumber);
					FString ResolveBaseFile = ClientRecord(VarName);
					VarName = FString::Printf(TEXT("resolveFromFile%d"), ResolveActionNumber);
					FString ResolveFromFile = ClientRecord(VarName);
					if(!ensureMsgf( ResolveFromFile == ResolveBaseFile, TEXT("Text cannot resolve %s with %s, we do not support cross file merging"), *ResolveBaseFile, *ResolveFromFile ) )
					{
						break;
					}

					VarName = FString::Printf(TEXT("resolveBaseRev%d"), ResolveActionNumber);
					FString ResolveBaseRev = ClientRecord(VarName);

					TTypeFromString<int>::FromString(State.PendingResolveRevNumber, *ResolveBaseRev);
					
					++ResolveActionNumber;
				}
			}
		}

		// Check binary status
		State.bBinary = false;
		if (HeadType.Len() > 0 && HeadType.Contains(TEXT("binary")))
		{
			State.bBinary = true;
		}

		// Check exclusive checkout flag
		State.bExclusiveCheckout = false;
		if (HeadType.Len() > 0 && HeadType.Contains(TEXT("+l")))
		{
			State.bExclusiveCheckout = true;
		}
	}

	// also see if we can glean anything from the error messages
	for (int32 Index = 0; Index < ErrorMessages.Num(); ++Index)
	{
		const FText& Error = ErrorMessages[Index];

		//@todo P4 could be returning localized error messages
		int32 NoSuchFilePos = Error.ToString().Find(TEXT(" - no such file(s).\n"), ESearchCase::IgnoreCase, ESearchDir::FromStart);
		if(NoSuchFilePos != INDEX_NONE)
		{
			// found an error about a file that is not in the depot
			FString FullPath(Error.ToString().Left(NoSuchFilePos));
			FPaths::NormalizeFilename(FullPath);
			OutStates.Add(FPerforceSourceControlState(FullPath));
			FPerforceSourceControlState& State = OutStates.Last();
			State.State = EPerforceState::NotInDepot;
		}

		//@todo P4 could be returning localized error messages
		int32 NotUnderClientRootPos = Error.ToString().Find(TEXT("' is not under client's root"), ESearchCase::IgnoreCase, ESearchDir::FromStart);
		if(NotUnderClientRootPos != INDEX_NONE)
		{
			// found an error about a file that is not under the client root
			static const FString Prefix(TEXT("Path \'"));
			FString FullPath(Error.ToString().Mid(Prefix.Len(), NotUnderClientRootPos - Prefix.Len()));
			FPaths::NormalizeFilename(FullPath);
			OutStates.Add(FPerforceSourceControlState(FullPath));
			FPerforceSourceControlState& State = OutStates.Last();
			State.State = EPerforceState::NotUnderClientRoot;	
		}
	}
}
コード例 #14
0
bool FPerforceConnection::RunCommand(const FString& InCommand, const TArray<FString>& InParameters, FP4RecordSet& OutRecordSet, TArray<FText>& OutErrorMessage, FOnIsCancelled InIsCancelled, bool& OutConnectionDropped, const bool bInStandardDebugOutput, const bool bInAllowRetry)
{
#if USE_P4_API
	if (!bEstablishedConnection)
	{
		return false;
	}

	FString FullCommand = InCommand;

	// Prepare arguments
	int32 ArgC = InParameters.Num();
	UTF8CHAR** ArgV = new UTF8CHAR*[ArgC];
	for (int32 Index = 0; Index < ArgC; Index++)
	{
		if(bIsUnicode)
		{
			FTCHARToUTF8 UTF8String(*InParameters[Index]);
			ArgV[Index] = new UTF8CHAR[UTF8String.Length() + 1];
			FMemory::Memcpy(ArgV[Index], UTF8String.Get(), UTF8String.Length() + 1);
		}
		else
		{
			ArgV[Index] = new UTF8CHAR[InParameters[Index].Len() + 1];
			FMemory::Memcpy(ArgV[Index], TCHAR_TO_ANSI(*InParameters[Index]), InParameters[Index].Len() + 1);
		}
		
		if (bInStandardDebugOutput)
		{
			FullCommand += TEXT(" ");
			FullCommand += InParameters[Index];
		}
	}

	if (bInStandardDebugOutput)
	{
		UE_LOG( LogSourceControl, Log, TEXT("Attempting 'p4 %s'"), *FullCommand );
	}

	double SCCStartTime = FPlatformTime::Seconds();

	P4Client.SetArgv(ArgC, (char**)ArgV);

	FP4KeepAlive KeepAlive(InIsCancelled);
	P4Client.SetBreak(&KeepAlive);

	OutRecordSet.Reset();
	FP4ClientUser User(OutRecordSet, bIsUnicode, OutErrorMessage);
	P4Client.Run(FROM_TCHAR(*InCommand, bIsUnicode), &User);
	if ( P4Client.Dropped() )
	{
		OutConnectionDropped = true;
	}

	P4Client.SetBreak(NULL);

	// Free arguments
	for (int32 Index = 0; Index < ArgC; Index++)
	{
		delete [] ArgV[Index];
	}
	delete [] ArgV;

	if (bInStandardDebugOutput)
	{
		UE_LOG( LogSourceControl, VeryVerbose, TEXT("P4 execution time: %0.4f seconds. Command: %s"), FPlatformTime::Seconds() - SCCStartTime, *FullCommand );
	}
#endif

	return OutRecordSet.Num() > 0;
}
コード例 #15
0
bool FPerforceConnection::GetWorkspaceList(const FPerforceConnectionInfo& InConnectionInfo, FOnIsCancelled InOnIsCanceled, TArray<FString>& OutWorkspaceList, TArray<FText>& OutErrorMessages)
{
	if(bEstablishedConnection)
	{
		TArray<FString> Params;
		bool bAllowWildHosts = !GIsBuildMachine;
		Params.Add(TEXT("-u"));
		Params.Add(InConnectionInfo.UserName);

		FP4RecordSet Records;
		bool bConnectionDropped = false;
		bool bCommandOK = RunCommand(TEXT("clients"), Params, Records, OutErrorMessages, InOnIsCanceled, bConnectionDropped);

		if (bCommandOK)
		{
			FString ApplicationPath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*FPaths::GameDir()).ToLower();

			FString LocalHostName = InConnectionInfo.HostOverride;
			if(LocalHostName.Len() == 0)
			{
				// No host override, check environment variable
				TCHAR P4HostEnv[256];
				FPlatformMisc::GetEnvironmentVariable(TEXT("P4HOST"), P4HostEnv, ARRAY_COUNT(P4HostEnv));
				LocalHostName = P4HostEnv;
			}

			if (LocalHostName.Len() == 0)
			{
				// no host name, use local machine name
				LocalHostName = FString(FPlatformProcess::ComputerName()).ToLower();
			}
			else
			{
				LocalHostName = LocalHostName.ToLower();
			}

			for (int32 Index = 0; Index < Records.Num(); ++Index)
			{
				const FP4Record& ClientRecord = Records[Index];
				FString ClientName = ClientRecord("client");
				FString HostName = ClientRecord("Host");
				FString ClientRootPath = ClientRecord("Root").ToLower();

				//this clientspec has to be meant for this machine ( "" hostnames mean any host can use ths clientspec in p4 land)
				bool bHostNameMatches = (LocalHostName == HostName.ToLower());
				bool bHostNameWild = (HostName.Len() == 0);

				if( bHostNameMatches || (bHostNameWild && bAllowWildHosts) )
				{
					// A workspace root could be "null" which allows the user to map depot locations to different drives.
					// Allow these workspaces since we already allow workspaces mapped to drive letters.
					const bool bIsNullClientRootPath = (ClientRootPath == TEXT("null"));

					//make sure all slashes point the same way
					ClientRootPath = ClientRootPath.Replace(TEXT("\\"), TEXT("/"));
					ApplicationPath = ApplicationPath.Replace(TEXT("\\"), TEXT("/"));

					if (!ClientRootPath.EndsWith(TEXT("/")))
					{
						ClientRootPath += TEXT("/");
					}

					// Only allow paths that are ACTUALLY legit for this application
					if (bIsNullClientRootPath || ApplicationPath.Contains(ClientRootPath) )
					{
						OutWorkspaceList.Add(ClientName);
					}
					else
					{
						UE_LOG(LogSourceControl, Error, TEXT(" %s client specs rejected due to root directory mismatch (%s)"), *ClientName, *ClientRootPath);
					}

					//Other useful fields: Description, Owner, Host

				}
				else
				{
					UE_LOG(LogSourceControl, Error, TEXT(" %s client specs rejected due to host name mismatch (%s)"), *ClientName, *HostName);
				}
			}
		}

		return bCommandOK;
	}

	return false;
}