Files
UnrealEngine/Engine/Source/ThirdParty/Perforce/P4Api.Net/p4bridge/P4BridgeClient.cpp
2025-05-18 13:04:45 +08:00

1446 lines
37 KiB
C++

/*******************************************************************************
Copyright (c) 2010, Perforce Software, Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/
/*******************************************************************************
* Name : P4BridgeClient.cpp
*
* Author : dbb
*
* Description : P4BridgeClient is a class derived from the ClientUser in the
* p4api. It provides the "UI" hooks for the p4api to return send output to the
* client app. It collects the output as a command is run so that the entire
* output can be retrieved when the command completes. Optionally, the client
* the client can register call back function to receive any or all of the
* output as it is generated by the p4api.
*
******************************************************************************/
#include "stdafx.h"
#include "P4BridgeClient.h"
#include "P4BridgeServer.h"
#include "P4Connection.h"
#include <strtable.h>
#include <strarray.h>
#include <diff.h>
class DiffObj : public Diff
{};
/*******************************************************************************
*
* P4BridgeClient
*
* Class derived from the ClientUser in the p4api. It provides the "UI" hooks
* for the p4api to return send output to the * client app. It collects
* the output as a command is run so that the entire * output can be
* retrieved when the command completes. Optionally, the client the client
* can register call back function to receive any or all of the output as
* it is generated by the p4api.
*
******************************************************************************/
/*******************************************************************************
*
* Constructor
*
* Initialize all the pointers to NULL. Space for storage will be created later
* when it is needed.
*
******************************************************************************/
P4BridgeClient::P4BridgeClient(P4BridgeServer* pserver, P4Connection* pcon)
: p4base(Type())
{
pCon = pcon;
pFirstError = NULL;
pLastError = NULL;
pFirstInfo = NULL;
pLastInfo = NULL;
info_results_count = 0;
results_dictionary_head = NULL;
results_dictionary_tail = NULL;
results_dictionary_count = 0;
data_set = NULL;
objId = 0;
pServer = pserver;
}
/*******************************************************************************
*
* Constructor
*
* Free any storage that till being used.
*
******************************************************************************/
P4BridgeClient::~P4BridgeClient(void)
{
clear_results();
}
/*******************************************************************************
*
* HandleException
*
* Handle any platform exceptions. The Microsoft Structured Exception Handler
* allows software to catch platform exceptions such as array overrun. The
* exception is logged, but the application will continue to run.
*
******************************************************************************/
/*******************************************************************************
*
* Message
*
* ClientUser override function that is called by the p4api. If the P4 server
* is not Unicode enabled, the default implementation will process the
* message by sending it's contents to HandleError() or OutputInfo(). For
* Unicode servers, this function must be overridden to provide the same
* functionality.
*
******************************************************************************/
void P4BridgeClient::Message( Error *err )
{
if (err->GetSeverity() >= E_WARN)
{
// This is an error
HandleError( err );
return; // no error
}
// not an error
if (err->GetSeverity() == E_INFO)
{
// This is an info message
ErrorId *id = err->GetId(0);
if (id == NULL)
return;
// Grab the text
StrBuf buf;
if( pServer->unicodeServer())
err->Fmt( buf, EF_PLAIN );
else
err->Fmt( buf, EF_PLAIN | EF_NOXLATE );
int msgCode = ErrorOf( id->Subsystem(), id->SubCode(), 0, 0, 0 );
HandleInfoMsg( id->code, (char)(err->GetGeneric() + '0'), buf.Text() );
return; // no error
}
}
/*******************************************************************************
*
* CallTextResultsCallbackFn
*
* Simple wrapper to call the callback function (if it has been set) within a
* SEH __try block to catch any platform exception. SEH __try blocks must
* be contained in simple functions or you will get Compiler Error C2712,
* "cannot use __try in functions that require object unwinding"
*
******************************************************************************/
void P4BridgeClient::CallTextResultsCallbackFn(const char *data)
{
pServer->CallTextResultsCallbackFn(pCon->getId(), data);
}
/*******************************************************************************
*
* OutputText
*
* ClientUser override function that is called when a P4 command produces text
* output. Calls the callback function if set and adds the new text to the
* existing results if any.
*
******************************************************************************/
void P4BridgeClient::OutputText( const char *data, int length )
{
CallTextResultsCallbackFn( data );
// length might not have been sent for null terminated string
if (data && (length < 0))
text_results.Append( data );
else
text_results.Append( data, length );
}
/*******************************************************************************
*
* CallInfoResultsCallbackFn
*
* Simple wrapper to call the callback function (if it has been set) within a
* SEH __try block to catch any platform exception. SEH __try blocks must
* be contained in simple functions or you will get Compiler Error C2712,
* "cannot use __try in functions that require object unwinding"
*
******************************************************************************/
void P4BridgeClient::CallInfoResultsCallbackFn( int msgId, char level, const char *data )
{
pServer->CallInfoResultsCallbackFn( pCon->getId(), msgId, level, data );
}
/*******************************************************************************
*
* OutputInfo
*
* ClientUser override function that is called when a P4 command produces
* information output (message severity level is E_INFO). Calls the callback
* function if set and adds the new text to the existing results if any.
*
******************************************************************************/
/*******************************************************************************
*
* CallTaggedOutputCallbackFn
*
* Simple wrapper to call the callback function (if it has been set) within a
* SEH __try block to catch any platform exception. SEH __try blocks must
* be contained in simple functions or you will get Compiler Error C2712,
* "cannot use __try in functions that require object unwinding"
*
******************************************************************************/
void P4BridgeClient::CallTaggedOutputCallbackFn( int objId, const char *pKey, const char * pVal )
{
pServer->CallTaggedOutputCallbackFn( pCon->getId(), objId, pKey, pVal );
}
/*******************************************************************************
*
* OutputStat
*
* ClientUser override function that is called when a P4 command produces
* tagged output. Calls the callback function if set and adds the new
* StrDict to the existing results if any.
*
******************************************************************************/
void P4BridgeClient::OutputStat( StrDict *dict )
{
StrDictList * pNew = new StrDictList();
if( results_dictionary_head == NULL )
{
// first item, so set as head and tail
results_dictionary_head = pNew;
results_dictionary_tail = pNew;
// set the object id
objId = 0;
results_dictionary_count = 0;
}
else
{
// add item to tail item and move tail pointer
results_dictionary_tail->Next(pNew);
results_dictionary_tail = pNew;
objId++;
results_dictionary_count++;
}
StrRef var, val;
for( int i = 0; dict->GetVar( i, var, val ); i++ )
{
// skip spec, specFormatted, and func tags
const char* key = var.Text();
if (strcmp(key, "spec") == 0 || strcmp(key, "specFormatted") == 0 || strcmp(key, "func") == 0)
continue;
char * pVal = new char[val.Length() + 2];
memcpy((void*)pVal, (void*) val.Text(), val.Length());
pVal[val.Length()] = '\0';
pVal[val.Length()+1] = '\0'; // if Unicode
pServer->CallTaggedOutputCallbackFn( pCon->getId(), objId, var.Text(), pVal );
delete[] pVal;
pNew->Data()->SetVar( var, val );
}
// flag the end of the object
CallTaggedOutputCallbackFn( objId, NULL, NULL );
return;
}
/*******************************************************************************
*
* CallErrorCallbackFn
*
* Simple wrapper to call the callback function (if it has been set) within a
* SEH __try block to catch any platform exception. SEH __try blocks must
* be contained in simple functions or you will get Compiler Error C2712,
* "cannot use __try in functions that require object unwinding"
*
******************************************************************************/
void P4BridgeClient::CallErrorCallbackFn( int severity, int errorId, const char * errMsg )
{
LOG_DEBUG(4, errMsg);
pServer->CallErrorCallbackFn(pCon->getId(), severity, errorId, errMsg);
}
/*******************************************************************************
*
* HandleError
*
* ClientUser override function that is called when a P4 command causes an
* error. Calls the callback function if set and adds the new error message
* to the existing errors for this command if any.
*
******************************************************************************/
void P4BridgeClient::HandleError( Error *err )
{
if (err->GetSeverity() == E_EMPTY)
return; // no error
StrBuf buf;
err->Fmt( buf, EF_NEWLINE );
// iterate through the ErrorIds in this Error
for(int i = 0; ; ++i)
{
ErrorId *id = err->GetId(i);
if (id == NULL)
break;
int eCode = ErrorOf( id->Subsystem(), id->SubCode(), 0, 0, 0 );
LOG_DEBUG(4, buf.Text());
HandleError( id->Severity(), id->code, buf.Text() );
}
}
/*******************************************************************************
*
* HandleError
*
* Internal function that is called when an error that needs to be reported to
* the client is generated in the bridge code. Calls the callback function
* if set and adds the new error message to the existing errors for this
* command if any.
*
******************************************************************************/
void P4BridgeClient::HandleError( int severity, int errorCode, const char *errMsg )
{
P4ClientError * pNewError = new P4ClientError(severity, errorCode, errMsg );
LOG_DEBUG(4, errMsg);
HandleError( pNewError );
}
void P4BridgeClient::HandleError( P4ClientError * pNewError )
{
LOG_DEBUG(4, pNewError->Message.c_str());
if( !pFirstError )
{
// first error so use it to start the list
pFirstError = pNewError;
pLastError = pNewError;
}
else
{
// Add it to the end of the list
pLastError->Next = pNewError;
pLastError = pNewError;
}
CallErrorCallbackFn( pNewError->Severity, pNewError->ErrorCode, pNewError->Message.c_str() );
}
/*******************************************************************************
*
* HandleInfoMsg
*
* Function that is called when an info message is received and reported to
* the client. Calls the callback function if set and adds the new info
* message to the existing info output for this command if any.
*
******************************************************************************/
void P4BridgeClient::HandleInfoMsg( int msgCode, char level, const char *infMsg )
{
P4ClientInfoMsg * pNewMsg = new P4ClientInfoMsg(msgCode, level, infMsg );
HandleInfoMsg( pNewMsg );
}
void P4BridgeClient::HandleInfoMsg( P4ClientInfoMsg * pNewMsg )
{
if( !pFirstInfo )
{
// first error so use it to start the list
pFirstInfo = pNewMsg;
pLastInfo = pNewMsg;
info_results_count = 0;
}
else
{
// Add it to the end of the list
pLastInfo->Next = pNewMsg;
pLastInfo = pNewMsg;
info_results_count++;
}
CallInfoResultsCallbackFn( pNewMsg->MsgCode, pNewMsg->Level, pNewMsg->Message.c_str() );
}
bool handleUrl = false;
void P4BridgeClient::HandleUrl(const StrPtr* url)
{
handleUrl = true;
ClientUser::HandleUrl(url);
}
/*******************************************************************************
*
* OutputError
*
* ClientUser override function that is called when an old P4 command causes an
* error. Calls the callback function if set and adds the new error message
* to the existing errors for this command if any.
*
******************************************************************************/
void P4BridgeClient::OutputError( const char *err )// For broken servers
{
LOG_DEBUG(4, err);
HandleError( -1, 0, err );
}
void P4BridgeClient::Diff( FileSys *f1, FileSys *f2, int doPage,
char *diffFlags, Error *e )
{
//
// Duck binary files. Much the same as ClientUser::Diff, we just
// put the output into Ruby space rather than stdout.
//
if( !f1->IsTextual() || !f2->IsTextual() )
{
if ( f1->Compare( f2, e ) )
OutputText( "(... files differ ...)", -1 );
return;
}
// Time to diff the two text files. Need to ensure that the
// files are in binary mode, so we have to create new FileSys
// objects to do this.
FileSys *f1_bin = FileSys::Create( FST_BINARY );
FileSys *f2_bin = FileSys::Create( FST_BINARY );
FileSys *t = FileSys::CreateGlobalTemp( f1->GetType() );
f1_bin->Set( f1->Name() );
f2_bin->Set( f2->Name() );
{
//
// In its own block to make sure that the diff object is deleted
// before we delete the FileSys objects.
//
DiffObj d;
d.SetInput( f1_bin, f2_bin, diffFlags, e );
if ( ! e->Test() ) d.SetOutput( t->Name(), e );
if ( ! e->Test() ) d.DiffWithFlags( diffFlags );
d.CloseOutput( e );
// OK, now we have the diff output, read it in and add it to
// the output.
if ( ! e->Test() ) t->Open( FOM_READ, e );
if ( ! e->Test() )
{
StrBuf b;
while( t->ReadLine( &b, e ) )
{
OutputText( b.Text(), b.Length() );
OutputText( "\r\n", -1 );
}
}
}
delete t;
delete f1_bin;
delete f2_bin;
if ( e->Test() ) HandleError( e );
}
/*******************************************************************************
*
* CallErrorCallbackFn
*
* Simple wrapper to call the callback function (if it has been set) within a
* SEH __try block to catch any platform exception. SEH __try blocks must
* be contained in simple functions or you will get Compiler Error C2712,
* "cannot use __try in functions that require object unwinding"
*
******************************************************************************/
void P4BridgeClient::CallBinaryResultsCallbackFn(void * data, int length )
{
pServer->CallBinaryResultsCallbackFn( pCon->getId(), data, length );
}
/*******************************************************************************
*
* OutputBinary
*
* ClientUser override function that is called when a P4 command produces
* binary data. Calls the callback function if set and adds the new data
* to the existing binary data for this command if any.
*
******************************************************************************/
void P4BridgeClient::OutputBinary( const char *data, int length )
{
CallBinaryResultsCallbackFn((void *) data, length );
Binary_results.insert(Binary_results.end(), data, data + length);
}
/*******************************************************************************
*
* GetErrorResults
*
* Gets the first error in the list of errors produced since the error list was
* last cleared. Returns null if there are no errors in the list. The
* P4ClientError object returned contains a pointer to the next error in
* the list, so the entire list can be obtained.
*
******************************************************************************/
P4ClientError* P4BridgeClient::GetErrorResults()
{
return pFirstError;
}
/*******************************************************************************
*
* GetInfoResultsCount
*
* Gets the count of the number info results collected since the results were
* last cleared. Returns -1 if there are no results available. The results
* are returned as a single string.
*
******************************************************************************/
int P4BridgeClient::GetInfoResultsCount()
{
return info_results_count;
}
/*******************************************************************************
*
* GetInfoResults
*
* Gets the first info results collected since the results were last cleared.
* Returns null if there are no results available. The results are
* returned as a single string.
*
******************************************************************************/
P4ClientInfoMsg * P4BridgeClient::GetInfoResults()
{
return pFirstInfo;
}
/*******************************************************************************
*
* GetTextResults
*
* Gets the first text results collected since the results were last cleared.
* Returns null if there are no results available. The results are
* returned as a single string.
*
******************************************************************************/
const char* P4BridgeClient::GetTextResults()
{
return text_results.Text();
}
/*******************************************************************************
*
* GetTaggedOutput
*
* Gets the first tagged output collected since the results were last cleared.
* Returns null if there are no results available. The StrDictListIterator
* object returned can be used to iterate through all of the objects and
* their values.
*
******************************************************************************/
StrDictListIterator* P4BridgeClient::GetTaggedOutput()
{
if (results_dictionary_head)
{
return new StrDictListIterator(results_dictionary_head);
}
return NULL;
}
/*******************************************************************************
*
* GetBinaryResults
*
* Gets the first info results collected since the results were last cleared.
* Returns null if there are no results available. The results are
* returned as a single buffer.
*
******************************************************************************/
const unsigned char* P4BridgeClient::GetBinaryResults()
{
return Binary_results.data();
}
/*******************************************************************************
*
* SetDataSet
*
* Called to set the Input Data which will be used by a command. The data must
* be set before the command is run
*
******************************************************************************/
void P4BridgeClient::SetDataSet(const char * data)
{
if (!data_set)
data_set = new StrBuf();
data_set->Set(data);
}
/*******************************************************************************
*
* GetDataSet
*
* Get the Input Data which will be used by a command.
*
******************************************************************************/
StrPtr * P4BridgeClient::GetDataSet( void )
{
return data_set;
}
void P4BridgeClient::Prompt( const StrPtr &msg, StrBuf &rsp,
int noEcho, Error *e )
{
pServer->Prompt(pCon->getId(), msg, rsp, noEcho, e);
}
/*******************************************************************************
*
* InputData
*
* ClientUser override function that is called when a P4 command requires
* additional text based data such as a workspace specification.
*
******************************************************************************/
void P4BridgeClient::InputData( StrBuf *buf, Error *err )
{
buf->Clear();
if (data_set)
buf->Set( data_set );
buf->Terminate();
}
/*******************************************************************************
*
* clear_results
*
* Clear all the stored results in preparation for running a command.
*
******************************************************************************/
#define DELETE_OBJECT(obj) if( obj != NULL ) { delete obj; obj = NULL; }
void P4BridgeClient::clear_results()
{
LOG_ENTRY();
// Cleanup the output buffers
DELETE_OBJECT( pFirstError )
pLastError = NULL;
DELETE_OBJECT( pFirstInfo )
pLastInfo = NULL;
DELETE_OBJECT( results_dictionary_head )
results_dictionary_tail = NULL;
text_results.Reset();
Binary_results.clear();
}
int P4BridgeClient::Resolve( ClientMerge *m, Error *e )
{
return pServer->Resolve( pCon->getId(), m, e );
}
int P4BridgeClient::Resolve( ClientResolveA *r, int preview, Error *e )
{
return pServer->Resolve(pCon->getId(), r, preview, e );
}
/*******************************************************************************
*
* StrDictList
*
* Class used to keep a list of StrDict objects when collecting the tagged
* output of a command
*
******************************************************************************/
/*******************************************************************************
* Constructor
******************************************************************************/
StrDictList::StrDictList()
: p4base(Type())
{
pStrDict = new StrBufDict();
pNext = NULL;
}
/*******************************************************************************
*
* StrDictListIterator
*
* Class used to iterate a list of StrDict objects when retrieving the tagged
* output of a command. See comments in p4bridge-api.cpp for a detailed
* usage explanation.
*
******************************************************************************/
/*******************************************************************************
* Constructor
******************************************************************************/
StrDictListIterator::StrDictListIterator()
: p4base(Type())
{
curEntry = NULL;
}
/*******************************************************************************
* Constructor
******************************************************************************/
StrDictListIterator::StrDictListIterator(StrDictList* ndict)
: p4base(Type())
{
curEntry = NULL;
Init(ndict);
}
/*******************************************************************************
* Destructor
*******************************************************************************
* Note: this destructor uses an iterative while loop to delete the list
* instead of recusion because a long list (> ~2000) can cause stack
* overflow with a recursive destructor.
******************************************************************************/
StrDictList::~StrDictList()
{
if (pStrDict != NULL)
delete pStrDict;
if (pNext == NULL)
{
return;
}
StrDictList* pCur = this->pNext;
while (pCur != NULL)
{
pNext = pCur->pNext;
pCur->pNext = NULL;
delete pCur;
pCur = this->pNext;
}
};
/*******************************************************************************
* Destructor
******************************************************************************/
StrDictListIterator::~StrDictListIterator()
{
Reset();
}
/*******************************************************************************
*
* Init
*
* Initialize the iterator to the beginning of the list before starting the
* iteration.
*
******************************************************************************/
int StrDictListIterator::Init(StrDictList* ndict)
{
dict = ndict;
Reset();
return (curItem == NULL);
}
/*******************************************************************************
*
* GetNextItem
*
* Get the next StrDict object in the list.
*
******************************************************************************/
StrDictList* StrDictListIterator::GetNextItem()
{
if (curItem == NULL)
{
curItem = dict;
}
else
{
curItem = curItem->Next();
}
// Start with the first entry of the new dictionary
idx = 0;
return curItem;
}
/*******************************************************************************
*
* GetEntry
*
* Get the nth key:value pair for the current StrDict object.
*
******************************************************************************/
KeyValuePair* StrDictListIterator::GetEntry(int idx)
{
if (curItem == NULL)
{
return NULL;
}
if (curEntry != NULL)
{
delete curEntry;
curEntry = NULL;
}
StrRef var, val;
if (curItem->Data()->GetVar( idx, var, val ))
curEntry = new KeyValuePair(var.Text(), var.Length(), val.Text(), val.Length());
return curEntry;
}
/*******************************************************************************
*
* GetNextEntry
*
* Get the next key:value pair for the current StrDict object.
*
******************************************************************************/
KeyValuePair* StrDictListIterator::GetNextEntry()
{
if (curItem == NULL)
{
return NULL;
}
return GetEntry(idx++);
}
/*******************************************************************************
*
* Reset
*
* Reset the iterator.
*
******************************************************************************/
void StrDictListIterator::Reset()
{
curItem = NULL;
idx = 0;
if (curEntry != NULL)
{
delete curEntry;
curEntry = NULL;
}
}
/*******************************************************************************
* KeyValuePair
******************************************************************************/
/*******************************************************************************
* Constructor
******************************************************************************/
KeyValuePair::KeyValuePair(const char * k, int kSz, const char * v, int vSz)
: p4base(Type()), keyLength(kSz), valLength(vSz)
{
key = k;
value = v;
}
/*******************************************************************************
* Destructor
******************************************************************************/
KeyValuePair::~KeyValuePair()
{
}
/*******************************************************************************
*
* P4ClientError
*
* This simple class is used to return the data elements of an error message.
*
******************************************************************************/
/*******************************************************************************
* Constructor
******************************************************************************/
P4ClientError::P4ClientError(int severity, int errorCode, const char * msg)
: p4base(Type())
{
LOG_DEBUG(4, msg);
Severity = severity;
ErrorCode = errorCode;
Message = msg;
Next = NULL;
}
P4ClientError::P4ClientError(P4ClientError * err)
: p4base(Type())
{
Severity = err->Severity;
ErrorCode = err->ErrorCode;
Message = err->Message;
Next = NULL;
}
/*******************************************************************************
* Destructor
*******************************************************************************
* Note: this destructor uses an iterative while loop to delete the list
* instead of recursion because a long list (> ~2000) can cause stack
* overflow with a recursive destructor.
******************************************************************************/
P4ClientError::~P4ClientError()
{
if (Next == NULL)
{
return;
}
P4ClientError* pCur = this->Next;
while (pCur != NULL)
{
this->Next = pCur->Next;
pCur->Next = NULL;
delete pCur;
pCur = this->Next;
}
};
/*******************************************************************************
* Destructor
******************************************************************************/
int P4ClientError::MaxSeverity()
{
P4ClientError *pNext = Next;
int maxSeverity = Severity;
while (pNext != NULL)
{
int nextSeverity = pNext->Severity;
if (nextSeverity > maxSeverity)
{
maxSeverity = nextSeverity;
}
pNext = pNext->Next;
}
return maxSeverity;
}
/*******************************************************************************
*
* P4ClientInfoMsg
*
* This simple class is used to return the data elements of an info message.
*
******************************************************************************/
/*******************************************************************************
* Constructor
******************************************************************************/
P4ClientInfoMsg::P4ClientInfoMsg(int msgCode, char level, const char * msg)
: p4base(Type())
{
Level = level;
MsgCode = msgCode;
Message = msg;
Next = NULL;
}
P4ClientInfoMsg::P4ClientInfoMsg(P4ClientInfoMsg * err)
: p4base(Type())
{
Level = err->Level;
MsgCode = err->MsgCode;
Message = err->Message;
Next = NULL;
}
/*******************************************************************************
* Destructor
*******************************************************************************
* Note: this destructor uses an iterative while loop to delete the list
* instead of recursion because a long list (> ~2000) can cause stack
* overflow with a recursive destructor.
******************************************************************************/
P4ClientInfoMsg::~P4ClientInfoMsg()
{
if (Next == NULL)
{
return;
}
P4ClientInfoMsg* pCur = this->Next;
while (pCur != NULL)
{
Next = pCur->Next;
pCur->Next = NULL;
delete pCur;
pCur = this->Next;
}
};
/*******************************************************************************
*
* P4ClientMerge
*
* This simple class is a wrapper for ClientMerge object.
*
******************************************************************************/
P4ClientMerge::P4ClientMerge(ClientMerge * merger)
: p4base(Type())
{
lastError = NULL;
Merger = merger;
}
P4ClientMerge::~P4ClientMerge()
{
if (lastError != NULL)
delete lastError;
}
P4ClientError *P4ClientMerge::GetLastError()
{
if (lastError != NULL)
{
delete lastError;
lastError = NULL;
}
if (err.GetSeverity() == E_EMPTY)
return NULL; // no error
StrBuf buf;
err.Fmt( buf, EF_NEWLINE );
lastError = new P4ClientError(err.GetSeverity(), err.GetGeneric(), buf.Text());
return lastError;
}
MergeStatus P4ClientMerge::AutoResolve( MergeForce forceMerge )
{
if (Merger == NULL)
return CMS_QUIT;
return Merger->AutoResolve(forceMerge);
}
MergeStatus P4ClientMerge::Resolve()
{
if (Merger == NULL)
return CMS_QUIT;
return Merger->Resolve(&err);
}
MergeStatus P4ClientMerge::DetectResolve()
{
if (Merger == NULL)
return CMS_QUIT;
return Merger->DetectResolve();
}
int P4ClientMerge::IsAcceptable()
{
if (Merger == NULL)
return false;
return Merger->IsAcceptable();
}
StrPtr *P4ClientMerge::GetBaseFile()
{
if (Merger == NULL)
return NULL;
FileSys *f = Merger->GetBaseFile();
if (f == NULL)
return NULL;
return f->Path();
}
StrPtr *P4ClientMerge::GetYourFile()
{
if (Merger == NULL)
return NULL;
FileSys *f = Merger->GetYourFile();
if (f == NULL)
return NULL;
return f->Path();
}
StrPtr *P4ClientMerge::GetTheirFile()
{
if (Merger == NULL)
return NULL;
FileSys *f = Merger->GetTheirFile();
if (f == NULL)
return NULL;
return f->Path();
}
StrPtr *P4ClientMerge::GetResultFile()
{
if (Merger == NULL)
return NULL;
FileSys *f = Merger->GetResultFile();
if (f == NULL)
return NULL;
return f->Path();
}
int P4ClientMerge::GetYourChunks()
{
if (Merger == NULL)
return -1;
return Merger->GetYourChunks();
}
int P4ClientMerge::GetTheirChunks()
{
if (Merger == NULL)
return -1;
return Merger->GetTheirChunks();
}
int P4ClientMerge::GetBothChunks()
{
if (Merger == NULL)
return -1;
return Merger->GetBothChunks();
}
int P4ClientMerge::GetConflictChunks()
{
if (Merger == NULL)
return -1;
return Merger->GetConflictChunks();
}
const StrPtr *P4ClientMerge::GetMergeDigest()
{
if (Merger == NULL)
return NULL;
return Merger->GetMergeDigest();
}
const StrPtr *P4ClientMerge::GetYourDigest()
{
if (Merger == NULL)
return NULL;
return Merger->GetYourDigest();
}
const StrPtr *P4ClientMerge::GetTheirDigest()
{
if (Merger == NULL)
return NULL;
return Merger->GetTheirDigest();
}
/*******************************************************************************
*
* P4ClientResolve
*
* This simple class is a wrapper for ClientResolve object.
*
******************************************************************************/
const Error P4ClientResolve::_INVALID_API_REFERENCE = Error();
P4ClientResolve::P4ClientResolve(ClientResolveA * resolver, int isUnicode) : p4base(Type())
{
lastError = NULL;
Resolver = resolver;
if (resolver != NULL)
{
Error er;
type.Clear();
er.Clear();
er = resolver->GetType();
if( isUnicode )
er.Fmt( type, EF_PLAIN );
else
er.Fmt( type, EF_PLAIN | EF_NOXLATE );
mergeAction.Clear();
er.Clear();
er = resolver->GetMergeAction();
if( isUnicode )
er.Fmt( mergeAction, EF_PLAIN );
else
er.Fmt( mergeAction, EF_PLAIN | EF_NOXLATE );
yoursAction.Clear();
er.Clear();
er = resolver->GetYoursAction();
if( isUnicode )
er.Fmt( yoursAction, EF_PLAIN );
else
er.Fmt( yoursAction, EF_PLAIN | EF_NOXLATE );
theirAction.Clear();
er.Clear();
er = resolver->GetTheirAction();
if( isUnicode )
er.Fmt( theirAction, EF_PLAIN );
else
er.Fmt( theirAction, EF_PLAIN | EF_NOXLATE );
mergePrompt.Clear();
er.Clear();
er = resolver->GetMergePrompt();
if( isUnicode )
er.Fmt( mergePrompt, EF_PLAIN );
else
er.Fmt( mergePrompt, EF_PLAIN | EF_NOXLATE );
yoursPrompt.Clear();
er.Clear();
er = resolver->GetYoursPrompt();
if( isUnicode )
er.Fmt( yoursPrompt, EF_PLAIN );
else
er.Fmt( yoursPrompt, EF_PLAIN | EF_NOXLATE );
theirPrompt.Clear();
er.Clear();
er = resolver->GetTheirPrompt();
if( isUnicode )
er.Fmt( theirPrompt, EF_PLAIN );
else
er.Fmt( theirPrompt, EF_PLAIN | EF_NOXLATE );
mergeOpt.Clear();
er.Clear();
er = resolver->GetMergeOpt();
if( isUnicode )
er.Fmt( mergeOpt, EF_PLAIN );
else
er.Fmt( mergeOpt, EF_PLAIN | EF_NOXLATE );
yoursOpt.Clear();
er.Clear();
er = resolver->GetYoursOpt();
if( isUnicode )
er.Fmt( yoursOpt, EF_PLAIN );
else
er.Fmt( yoursOpt, EF_PLAIN | EF_NOXLATE );
theirOpt.Clear();
er.Clear();
er = resolver->GetTheirOpt();
if( isUnicode )
er.Fmt( theirOpt, EF_PLAIN );
else
er.Fmt( theirOpt, EF_PLAIN | EF_NOXLATE );
skipOpt.Clear();
er.Clear();
er = resolver->GetSkipOpt();
if( isUnicode )
er.Fmt( skipOpt, EF_PLAIN );
else
er.Fmt( skipOpt, EF_PLAIN | EF_NOXLATE );
helpOpt.Clear();
er.Clear();
er = resolver->GetHelpOpt();
if( isUnicode )
er.Fmt( helpOpt, EF_PLAIN );
else
er.Fmt( helpOpt, EF_PLAIN | EF_NOXLATE );
autoOpt.Clear();
er.Clear();
er = resolver->GetAutoOpt();
if( isUnicode )
er.Fmt( autoOpt, EF_PLAIN );
else
er.Fmt( autoOpt, EF_PLAIN | EF_NOXLATE );
prompt.Clear();
er.Clear();
er = resolver->GetPrompt();
if( isUnicode )
er.Fmt( prompt, EF_PLAIN );
else
er.Fmt( prompt, EF_PLAIN | EF_NOXLATE );
typePrompt.Clear();
er.Clear();
er = resolver->GetTypePrompt();
if( isUnicode )
er.Fmt( typePrompt, EF_PLAIN );
else
er.Fmt( typePrompt, EF_PLAIN | EF_NOXLATE );
usageError.Clear();
er.Clear();
er = resolver->GetUsageError();
if( isUnicode )
er.Fmt( usageError, EF_PLAIN );
else
er.Fmt( usageError, EF_PLAIN | EF_NOXLATE );
help.Clear();
er.Clear();
er = resolver->GetHelp();
if( isUnicode )
er.Fmt( help, EF_PLAIN );
else
er.Fmt( help, EF_PLAIN | EF_NOXLATE );
}
}
P4ClientResolve::~P4ClientResolve()
{
if (lastError != NULL)
delete lastError;
}
P4ClientError *P4ClientResolve::GetLastError()
{
if (lastError != NULL)
{
delete lastError;
lastError = NULL;
}
if (err.GetSeverity() == E_EMPTY)
return NULL; // no error
StrBuf buf;
err.Fmt( buf, EF_NEWLINE );
lastError = new P4ClientError(err.GetSeverity(), err.GetGeneric(), buf.Text());
return lastError;
}
MergeStatus P4ClientResolve::AutoResolve( MergeForce force ) const
{
if (Resolver == NULL)
{
return CMS_SKIP;
}
return Resolver->AutoResolve( force );
}
MergeStatus P4ClientResolve::Resolve( int preview )
{
if (Resolver == NULL)
{
return CMS_SKIP;
}
return Resolver->Resolve( preview, &err );
}