1446 lines
37 KiB
C++
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 );
|
|
}
|