/******************************************************************************* 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 "ClientManager.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; results_dictionary_head = NULL; results_dictionary_tail = NULL; data_set = NULL; // info_results = NULL; text_results = NULL; Binary_results = NULL; objId = 0; if (pCon) { pCon->ui = this; } 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. * ******************************************************************************/ //int P4BridgeClient::HandleException(unsigned int c, struct _EXCEPTION_POINTERS *e) //{ // unsigned int code = c; // struct _EXCEPTION_POINTERS *ep = e; // // // Log the exception // char * exType = "Unknown"; // // #if _WIN32 // switch (code) // { // case EXCEPTION_ACCESS_VIOLATION: // exType = "EXCEPTION_ACCESS_VIOLATION\r\n"; // break; // case EXCEPTION_DATATYPE_MISALIGNMENT: // exType = "EXCEPTION_DATATYPE_MISALIGNMENT\r\n"; // break; // case EXCEPTION_BREAKPOINT: // exType = "EXCEPTION_BREAKPOINT\r\n"; // break; // case EXCEPTION_SINGLE_STEP: // exType = "EXCEPTION_SINGLE_STEP\r\n"; // break; // case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: // exType = "EXCEPTION_ARRAY_BOUNDS_EXCEEDED\r\n"; // break; // case EXCEPTION_FLT_DENORMAL_OPERAND: // exType = "EXCEPTION_FLT_DENORMAL_OPERAND\r\n"; // break; // case EXCEPTION_FLT_DIVIDE_BY_ZERO: // exType = "EXCEPTION_FLT_DIVIDE_BY_ZERO\r\n"; // break; // case EXCEPTION_FLT_INEXACT_RESULT: // exType = "EXCEPTION_FLT_INEXACT_RESULT\r\n"; // break; // case EXCEPTION_FLT_INVALID_OPERATION: // exType = "EXCEPTION_FLT_INVALID_OPERATION\r\n"; // break; // case EXCEPTION_FLT_OVERFLOW: // exType = "EXCEPTION_FLT_OVERFLOW\r\n"; // break; // case EXCEPTION_FLT_STACK_CHECK: // exType = "EXCEPTION_FLT_STACK_CHECK\r\n"; // break; // case EXCEPTION_FLT_UNDERFLOW: // exType = "EXCEPTION_FLT_UNDERFLOW\r\n"; // break; // case EXCEPTION_INT_DIVIDE_BY_ZERO: // exType = "EXCEPTION_INT_DIVIDE_BY_ZERO\r\n"; // break; // case EXCEPTION_INT_OVERFLOW: // exType = "EXCEPTION_INT_OVERFLOW\r\n"; // break; // case EXCEPTION_PRIV_INSTRUCTION: // exType = "EXCEPTION_PRIV_INSTRUCTION\r\n"; // break; // case EXCEPTION_IN_PAGE_ERROR: // exType = "EXCEPTION_IN_PAGE_ERROR\r\n"; // break; // case EXCEPTION_ILLEGAL_INSTRUCTION: // exType = "EXCEPTION_ILLEGAL_INSTRUCTION\r\n"; // break; // case EXCEPTION_NONCONTINUABLE_EXCEPTION: // exType = "EXCEPTION_NONCONTINUABLE_EXCEPTION\r\n"; // break; // case EXCEPTION_STACK_OVERFLOW: // exType = "EXCEPTION_STACK_OVERFLOW\r\n"; // break; // case EXCEPTION_INVALID_DISPOSITION: // exType = "EXCEPTION_INVALID_DISPOSITION\r\n"; // break; // case EXCEPTION_GUARD_PAGE: // exType = "EXCEPTION_GUARD_PAGE\r\n"; // break; // case EXCEPTION_INVALID_HANDLE: // exType = "EXCEPTION_INVALID_HANDLE\r\n"; // break; // //case EXCEPTION_POSSIBLE_DEADLOCK: // // exType = "EXCEPTION_POSSIBLE_DEADLOCK\r\n"); // // break; // default: // printf("UNKOWN EXCEPTION\r\n"); // break; // } // #endif // if (ExceptionError != NULL) // delete ExceptionError; // // ExceptionError = new StrBuf(); // // ExceptionError->Append("Exception Detected in callback function: "); // ExceptionError->Append(exType); // // return EXCEPTION_EXECUTE_HANDLER; //} /******************************************************************************* * * 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 //StrDict * eDict = err->GetDict(); //OutputStat( eDict ); //err->Dump(":dump:"); 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 ss = id->Subsystem(); //int sc = id->SubCode(); 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->Id, 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 ); if( !text_results ) text_results = new StrBuf(); // 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->Id, 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. * ******************************************************************************/ //void P4BridgeClient::OutputInfo( char level, const char *data ) //{ // CallInfoResultsCallbackFn( level, data ); // // if( !info_results ) // info_results = new StrBuf; // // char * lvl = new char[3]; // lvl[0] = level; // lvl[1] = ':' ; // lvl[2] = '\0' ; // info_results->Append( lvl ); // info_results->Append( data ); // info_results->Append( "\r\n" ); // // delete[] lvl; //} /******************************************************************************* * * 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->Id, 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; } else { // add item to tail item and move tail pointer results_dictionary_tail->Next(pNew); results_dictionary_tail = pNew; objId++; } StrRef var, val; for( int i = 0; dict->GetVar( i, var, val ); i++ ) { 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->Id, 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 ) { pServer->CallErrorCallbackFn(pCon->Id, 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 ss = id->Subsystem(); //int sc = id->SubCode(); int eCode = ErrorOf( id->Subsystem(), id->SubCode(), 0, 0, 0 ); 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 ) { //LOG_DEBUG3(4,"P4BridgeClient::HandleError %d %x %s",severity, errorCode, errMsg); P4ClientError * pNewError = new P4ClientError(severity, errorCode, errMsg ); HandleError( pNewError ); } void P4BridgeClient::HandleError( P4ClientError * pNewError ) { 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 ); } /******************************************************************************* * * 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; } else { // Add it to the end of the list pLastInfo->Next = pNewMsg; pLastInfo = pNewMsg; } CallInfoResultsCallbackFn( pNewMsg->MsgCode, pNewMsg->Level, pNewMsg->Message ); } /******************************************************************************* * * 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 { 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->Id, 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 ); if (Binary_results) { char * oldResults = Binary_results; Binary_results = AddStrBuff(Binary_results, Binary_results_Count, data, length); Binary_results_Count += length; delete[] oldResults; // need to delete the old buffer for results return; } Binary_results = CpyStrBuff(data, length ); Binary_results_Count = 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; } /******************************************************************************* * * 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. * ******************************************************************************/ StrBuf* P4BridgeClient::GetTextResults() { return text_results; } /******************************************************************************* * * 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. * ******************************************************************************/ void * P4BridgeClient::GetBinaryResults() { return Binary_results; } /******************************************************************************* * * 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->Id, 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() { // Cleanup the output buffers DELETE_OBJECT( pFirstError ) pLastError = NULL; DELETE_OBJECT( text_results ) DELETE_OBJECT( pFirstInfo ) pLastInfo = NULL; DELETE_OBJECT( results_dictionary_head ) results_dictionary_tail = NULL; DELETE_OBJECT( Binary_results ); Binary_results_Count = 0; // Other Data // DELETE_OBJECT( data_set ) // DELETE_OBJECT( ExceptionError); } int P4BridgeClient::Resolve( ClientMerge *m, Error *e ) { return pServer->Resolve( pCon->Id, m, e ); } int P4BridgeClient::Resolve( ClientResolveA *r, int preview, Error *e ) { //Error er = r->GetType(); ////const Error& er = r->GetType(); //StrBuf msg; //if( isUnicode ) // er.Fmt( msg, EF_PLAIN ); //else // er.Fmt( msg, EF_PLAIN | EF_NOXLATE ); //msg.Clear(); //er.Clear(); //er = r->GetMergeAction(); //if( isUnicode ) // er.Fmt( msg, EF_PLAIN ); //else // er.Fmt( msg, EF_PLAIN | EF_NOXLATE ); //msg.Clear(); //er.Clear(); //er = r->GetYoursAction(); //if( isUnicode ) // er.Fmt( msg, EF_PLAIN ); //else // er.Fmt( msg, EF_PLAIN | EF_NOXLATE ); //msg.Clear(); //er.Clear(); //er = r->GetTheirAction(); //if( isUnicode ) // er.Fmt( msg, EF_PLAIN ); //else // er.Fmt( msg, EF_PLAIN | EF_NOXLATE ); //msg.Clear(); //er.Clear(); //er = r->GetMergePrompt(); //if( isUnicode ) // er.Fmt( msg, EF_PLAIN ); //else // er.Fmt( msg, EF_PLAIN | EF_NOXLATE ); //msg.Clear(); //er.Clear(); //er = r->GetYoursPrompt(); //if( isUnicode ) // er.Fmt( msg, EF_PLAIN ); //else // er.Fmt( msg, EF_PLAIN | EF_NOXLATE ); //msg.Clear(); //er.Clear(); //er = r->GetTheirPrompt(); //if( isUnicode ) // er.Fmt( msg, EF_PLAIN ); //else // er.Fmt( msg, EF_PLAIN | EF_NOXLATE ); //msg.Clear(); //er.Clear(); //er = r->GetMergeOpt(); //if( isUnicode ) // er.Fmt( msg, EF_PLAIN ); //else // er.Fmt( msg, EF_PLAIN | EF_NOXLATE ); //msg.Clear(); //er.Clear(); //er = r->GetYoursOpt(); //if( isUnicode ) // er.Fmt( msg, EF_PLAIN ); //else // er.Fmt( msg, EF_PLAIN | EF_NOXLATE ); //msg.Clear(); //er.Clear(); //er = r->GetTheirOpt(); //if( isUnicode ) // er.Fmt( msg, EF_PLAIN ); //else // er.Fmt( msg, EF_PLAIN | EF_NOXLATE ); //msg.Clear(); //er.Clear(); //er = r->GetSkipOpt(); //if( isUnicode ) // er.Fmt( msg, EF_PLAIN ); //else // er.Fmt( msg, EF_PLAIN | EF_NOXLATE ); //msg.Clear(); //er.Clear(); //er = r->GetHelpOpt(); //if( isUnicode ) // er.Fmt( msg, EF_PLAIN ); //else // er.Fmt( msg, EF_PLAIN | EF_NOXLATE ); //msg.Clear(); //er.Clear(); //er = r->GetAutoOpt() ; //if( isUnicode ) // er.Fmt( msg, EF_PLAIN ); //else // er.Fmt( msg, EF_PLAIN | EF_NOXLATE ); //msg.Clear(); //er.Clear(); //er = r->GetPrompt(); //if( isUnicode ) // er.Fmt( msg, EF_PLAIN ); //else // er.Fmt( msg, EF_PLAIN | EF_NOXLATE ); //msg.Clear(); //er.Clear(); //er = r->GetTypePrompt(); //if( isUnicode ) // er.Fmt( msg, EF_PLAIN ); //else // er.Fmt( msg, EF_PLAIN | EF_NOXLATE ); //msg.Clear(); //er.Clear(); //er = r->GetUsageError(); //if( isUnicode ) // er.Fmt( msg, EF_PLAIN ); //else // er.Fmt( msg, EF_PLAIN | EF_NOXLATE ); //msg.Clear(); //er.Clear(); //er = r->GetHelp(); //if( isUnicode ) // er.Fmt( msg, EF_PLAIN ); //else // er.Fmt( msg, EF_PLAIN | EF_NOXLATE ); //msg.Clear(); //er.Clear(); //er = r->GetType(); //if( isUnicode ) // er.Fmt( msg, EF_PLAIN ); //else // er.Fmt( msg, EF_PLAIN | EF_NOXLATE ); //Error er2 = r->GetHelp(); ////const Error& er = r->GetType(); //StrBuf msg2; //if( isUnicode ) // er2.Fmt( msg2, EF_PLAIN ); //else // er2.Fmt( msg2, EF_PLAIN | EF_NOXLATE ); //Error er3 = r->GetType(); ////const Error& er = r->GetType(); //StrBuf msg3; //if( isUnicode ) // er3.Fmt( msg3, EF_PLAIN ); //else // er3.Fmt( msg3, EF_PLAIN | EF_NOXLATE ); //msg.Clear(); //er.Clear(); return pServer->Resolve(pCon->Id, 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 = CopyStr(k); value = CopyStr(v); } /******************************************************************************* * Destructor ******************************************************************************/ KeyValuePair::~KeyValuePair() { if (key) delete[] key; if (value) delete[] value; } /******************************************************************************* * * 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()) { Severity = severity; ErrorCode = errorCode; Message = CopyStr(msg); Next = NULL; } P4ClientError::P4ClientError(P4ClientError * err) : p4base(Type()) { Severity = err->Severity; ErrorCode = err->ErrorCode; Message = CopyStr(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 (Message != NULL) delete[] Message; if (Next == NULL) { return; } P4ClientError* pCur = this->Next; while (pCur != NULL) { this->Next = pCur->Next; pCur->Next = NULL; delete pCur; pCur = this->Next; } }; /******************************************************************************* * Destructor ******************************************************************************/ //P4ClientError::~P4ClientError() //{ // if (Message) delete Message; // // if (Next) delete Next; //} 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 = CopyStr(msg); Next = NULL; } P4ClientInfoMsg::P4ClientInfoMsg(P4ClientInfoMsg * err) : p4base(Type()) { Level = err->Level; MsgCode = err->MsgCode; Message = CopyStr(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 (Message != NULL) delete[] Message; 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; //lastValue = 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; //if (lastValue != NULL) // delete lastValue; } 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 ); } //P4ClientError *P4ClientResolve::GetType() //{ // if (lastValue != NULL) // { // delete lastValue; // lastValue = NULL; // } // if (Resolver == NULL) // { // return NULL; // } // const Error &value = Resolver->GetType(); // if (value.GetSeverity() == E_EMPTY) // return NULL; // no error // StrBuf buf; // value.Fmt( buf, EF_NEWLINE ); // lastValue = new P4ClientError(value.GetSeverity(), buf.Text()); // return lastValue; //} //P4ClientError *P4ClientResolve::GetMergeAction() //{ // if (lastValue != NULL) // { // delete lastValue; // lastValue = NULL; // } // if (Resolver == NULL) // { // return NULL; // } // const Error &value = Resolver->GetMergeAction(); // if (value.GetSeverity() == E_EMPTY) // return NULL; // no error // StrBuf buf; // value.Fmt( buf, EF_NEWLINE ); // lastValue = new P4ClientError(value.GetSeverity(), buf.Text()); // return lastValue; //} //P4ClientError *P4ClientResolve::GetYoursAction() //{ // if (lastValue != NULL) // { // delete lastValue; // lastValue = NULL; // } // if (Resolver == NULL) // { // return NULL; // } // const Error &value = Resolver->GetYoursAction(); // if (value.GetSeverity() == E_EMPTY) // return NULL; // no error // StrBuf buf; // value.Fmt( buf, EF_NEWLINE ); // lastValue = new P4ClientError(value.GetSeverity(), buf.Text()); // return lastValue; //} //P4ClientError *P4ClientResolve::GetTheirAction() //{ // if (lastValue != NULL) // { // delete lastValue; // lastValue = NULL; // } // if (Resolver == NULL) // { // return NULL; // } // const Error &value = Resolver->GetTheirAction(); // if (value.GetSeverity() == E_EMPTY) // return NULL; // no error // StrBuf buf; // value.Fmt( buf, EF_NEWLINE ); // lastValue = new P4ClientError(value.GetSeverity(), buf.Text()); // return lastValue; //} //// For the CLI interface, probably not of interest to others //P4ClientError *P4ClientResolve::GetMergePrompt() //{ // if (lastValue != NULL) // { // delete lastValue; // lastValue = NULL; // } // if (Resolver == NULL) // { // return NULL; // } // const Error &value = Resolver->GetMergePrompt(); // if (value.GetSeverity() == E_EMPTY) // return NULL; // no error // StrBuf buf; // value.Fmt( buf, EF_NEWLINE ); // lastValue = new P4ClientError(value.GetSeverity(), buf.Text()); // return lastValue; //} //P4ClientError *P4ClientResolve::GetYoursPrompt() //{ // if (lastValue != NULL) // { // delete lastValue; // lastValue = NULL; // } // if (Resolver == NULL) // { // return NULL; // } // const Error &value = Resolver->GetYoursPrompt(); // if (value.GetSeverity() == E_EMPTY) // return NULL; // no error // StrBuf buf; // value.Fmt( buf, EF_NEWLINE ); // lastValue = new P4ClientError(value.GetSeverity(), buf.Text()); // return lastValue; //} //P4ClientError *P4ClientResolve::GetTheirPrompt() //{ // if (lastValue != NULL) // { // delete lastValue; // lastValue = NULL; // } // if (Resolver == NULL) // { // return NULL; // } // const Error &value = Resolver->GetTheirPrompt(); // if (value.GetSeverity() == E_EMPTY) // return NULL; // no error // StrBuf buf; // value.Fmt( buf, EF_NEWLINE ); // lastValue = new P4ClientError(value.GetSeverity(), buf.Text()); // return lastValue; //} //P4ClientError *P4ClientResolve::GetMergeOpt() //{ // if (lastValue != NULL) // { // delete lastValue; // lastValue = NULL; // } // if (Resolver == NULL) // { // return NULL; // } // const Error &value = Resolver->GetMergeOpt(); // if (value.GetSeverity() == E_EMPTY) // return NULL; // no error // StrBuf buf; // value.Fmt( buf, EF_NEWLINE ); // lastValue = new P4ClientError(value.GetSeverity(), buf.Text()); // return lastValue; //} //P4ClientError *P4ClientResolve::GetYoursOpt() //{ // if (lastValue != NULL) // { // delete lastValue; // lastValue = NULL; // } // if (Resolver == NULL) // { // return NULL; // } // const Error &value = Resolver->GetYoursOpt(); // if (value.GetSeverity() == E_EMPTY) // return NULL; // no error // StrBuf buf; // value.Fmt( buf, EF_NEWLINE ); // lastValue = new P4ClientError(value.GetSeverity(), buf.Text()); // return lastValue; //} //P4ClientError *P4ClientResolve::GetTheirOpt() //{ // if (lastValue != NULL) // { // delete lastValue; // lastValue = NULL; // } // if (Resolver == NULL) // { // return NULL; // } // const Error &value = Resolver->GetTheirOpt(); // if (value.GetSeverity() == E_EMPTY) // return NULL; // no error // StrBuf buf; // value.Fmt( buf, EF_NEWLINE ); // lastValue = new P4ClientError(value.GetSeverity(), buf.Text()); // return lastValue; //} //P4ClientError *P4ClientResolve::GetSkipOpt() //{ // if (lastValue != NULL) // { // delete lastValue; // lastValue = NULL; // } // if (Resolver == NULL) // { // return NULL; // } // const Error &value = Resolver->GetSkipOpt(); // if (value.GetSeverity() == E_EMPTY) // return NULL; // no error // StrBuf buf; // value.Fmt( buf, EF_NEWLINE ); // lastValue = new P4ClientError(value.GetSeverity(), buf.Text()); // return lastValue; //} //P4ClientError *P4ClientResolve::GetHelpOpt() //{ // if (lastValue != NULL) // { // delete lastValue; // lastValue = NULL; // } // if (Resolver == NULL) // { // return NULL; // } // const Error &value = Resolver->GetHelpOpt(); // if (value.GetSeverity() == E_EMPTY) // return NULL; // no error // StrBuf buf; // value.Fmt( buf, EF_NEWLINE ); // lastValue = new P4ClientError(value.GetSeverity(), buf.Text()); // return lastValue; //} //P4ClientError *P4ClientResolve::GetAutoOpt() //{ // if (lastValue != NULL) // { // delete lastValue; // lastValue = NULL; // } // if (Resolver == NULL) // { // return NULL; // } // const Error &value = Resolver->GetAutoOpt(); // if (value.GetSeverity() == E_EMPTY) // return NULL; // no error // StrBuf buf; // value.Fmt( buf, EF_NEWLINE ); // lastValue = new P4ClientError(value.GetSeverity(), buf.Text()); // return lastValue; //} //P4ClientError *P4ClientResolve::GetPrompt() //{ // if (lastValue != NULL) // { // delete lastValue; // lastValue = NULL; // } // if (Resolver == NULL) // { // return NULL; // } // const Error &value = Resolver->GetPrompt(); // if (value.GetSeverity() == E_EMPTY) // return NULL; // no error // StrBuf buf; // value.Fmt( buf, EF_NEWLINE ); // lastValue = new P4ClientError(value.GetSeverity(), buf.Text()); // return lastValue; //} //P4ClientError *P4ClientResolve::GetTypePrompt() //{ // if (lastValue != NULL) // { // delete lastValue; // lastValue = NULL; // } // if (Resolver == NULL) // { // return NULL; // } // const Error &value = Resolver->GetTypePrompt(); // if (value.GetSeverity() == E_EMPTY) // return NULL; // no error // StrBuf buf; // value.Fmt( buf, EF_NEWLINE ); // lastValue = new P4ClientError(value.GetSeverity(), buf.Text()); // return lastValue; //} //P4ClientError *P4ClientResolve::GetUsageError() //{ // if (lastValue != NULL) // { // delete lastValue; // lastValue = NULL; // } // if (Resolver == NULL) // { // return NULL; // } // const Error &value = Resolver->GetUsageError(); // if (value.GetSeverity() == E_EMPTY) // return NULL; // no error // StrBuf buf; // value.Fmt( buf, EF_NEWLINE ); // lastValue = new P4ClientError(value.GetSeverity(), buf.Text()); // return lastValue; //} //P4ClientError *P4ClientResolve::GetHelp() //{ // if (lastValue != NULL) // { // delete lastValue; // lastValue = NULL; // } // if (Resolver == NULL) // { // return NULL; // } // const Error &value = Resolver->GetHelp(); // if (value.GetSeverity() == E_EMPTY) // return NULL; // no error // StrBuf buf; // value.Fmt( buf, EF_NEWLINE ); // lastValue = new P4ClientError(value.GetSeverity(), buf.Text()); // return lastValue; //}
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 19640 | Liz Lam | "Forking branch Main of perforce-software-p4connect to liz_lam-p4connect." | ||
//guest/perforce_software/p4connect/main/src/P4Bridge/p4bridge/P4BridgeClient.cpp | |||||
#1 | 16209 | Norman Morse | Move entire source tree into "main" branch so workshop code will act correctly. | ||
//guest/perforce_software/p4connect/src/P4Bridge/p4bridge/P4BridgeClient.cpp | |||||
#2 | 12135 | Norman Morse |
Integrate dev branch changes into main. This code is the basiis of the 2.7 BETA release which provides Unity 5 compatibility |
||
#1 | 10940 | Norman Morse |
Inital Workshop release of P4Connect. Released under BSD-2 license |