/******************************************************************************* Copyright (c) 2003, Robert Cowham and Vaccaperna Systems Ltd. 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 VACCAPERNA SYSTEMS LTD. 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: p4ClientUser.cpp * * Author: Robert Cowham <robert@vaccaperna.co.uk> * * Description: * COM bindings for the Perforce API. User interface class * for getting Perforce results into COM. * ******************************************************************************/ #include "stdafx.h" #include "p4ClientUser.h" #include "p4interface.h" #include "TraceUtils.h" #include "i18napi.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// p4ClientUser::p4ClientUser() : m_BinaryFile(NULL), m_TextFile(NULL), m_spec(NULL), m_specData(NULL), m_TranslateCharset(0), m_tranbuf(NULL), m_InfoArray(new StrBufArray()), m_WarningArray(new StrBufArray()), m_ErrorArray(new StrBufArray()) { try { STACK_TRACE("p4ClientUser::p4ClientUser") } catch (...) { LogException(_T("p4ClientUser::p4ClientUser")); } } void p4ClientUser::Reset() { try { STACK_TRACE("p4ClientUser::Reset") // Ensure our arrays are OK. m_InfoArray->Clear(); m_WarningArray->Clear(); m_ErrorArray->Clear(); CloseFiles(); } catch (...) { LogException(_T("p4ClientUser::Reset")); } } p4ClientUser::~p4ClientUser() { try { STACK_TRACE("p4ClientUser::~p4ClientUser") if (m_spec) delete m_spec; if (m_specData) delete m_specData; if (m_tranbuf) delete m_tranbuf; if (m_InfoArray) delete m_InfoArray; if (m_ErrorArray) delete m_ErrorArray; if (m_WarningArray) delete m_WarningArray; CloseFiles(); } catch (...) { LogException(_T("p4ClientUser::~p4ClientUser")); } } // From BSTR to UTF8 string (if appropriate) LPCSTR p4ClientUser::TranslateFromBSTR(wstring &bs) { USES_CONVERSION; if (!TranslateCharset()) { m_buf.Set(W2CA(bs.c_str())); return m_buf.Text(); } else { // Find out how big a buffer we need int buflen = WideCharToMultiByte(CP_UTF8, 0, bs.c_str(), bs.length(), NULL, 0, NULL, NULL); if (m_tranbuf != NULL) delete m_tranbuf; m_tranbuf = new char[buflen + 1]; int copied = WideCharToMultiByte(CP_UTF8, 0, bs.data(), bs.length(), m_tranbuf, buflen + 1, NULL, NULL); if (0 == copied) throw DISP_E_EXCEPTION; m_tranbuf[copied] = '\0'; // Make sure NULL terminated as problems otherwise return m_tranbuf; } } LPCSTR p4ClientUser::TranslateFromBSTR(BSTR bs) { USES_CONVERSION; if (!TranslateCharset()) { m_buf.Set(W2CA(bs)); return m_buf.Text(); } else { // Find out how big a buffer we need int buflen = WideCharToMultiByte(CP_UTF8, 0, bs, SysStringLen(bs), NULL, 0, NULL, NULL); if (m_tranbuf != NULL) delete m_tranbuf; m_tranbuf = new char[buflen + 1]; int copied = WideCharToMultiByte(CP_UTF8, 0, bs, SysStringLen(bs), m_tranbuf, buflen + 1, NULL, NULL); if (0 == copied) throw DISP_E_EXCEPTION; m_tranbuf[copied] = '\0'; // Make sure NULL terminated as problems otherwise return m_tranbuf; } } // From UTF8 string to BSTR (if appropriate) BSTR p4ClientUser::TranslateToBSTR(StrBuf *sb) { USES_CONVERSION; CComBSTR result; if (!TranslateCharset()) { result = A2CW(sb->Text()); } else { if (sb->Length()) { // Find out how big a buffer we need int buflen = MultiByteToWideChar(CP_UTF8, 0, sb->Text(), sb->Length(), NULL, 0); LPWSTR lpWideCharStr = new WCHAR[buflen + 1]; int copied = MultiByteToWideChar(CP_UTF8, 0, sb->Text(), sb->Length(), lpWideCharStr, buflen + 1); if (0 == copied) { delete [] lpWideCharStr; throw DISP_E_EXCEPTION; } lpWideCharStr[buflen] = '\0'; // Now assign to ourselves result = lpWideCharStr; delete [] lpWideCharStr; } } return result.Detach(); } void p4ClientUser::TranslateCharset(int newVal) { STACK_TRACE("p4ClientUser::TranslateCharset set") m_TranslateCharset = newVal; } int p4ClientUser::TranslateCharset() { STACK_TRACE("p4ClientUser::TranslateCharset get") return m_TranslateCharset; } void p4ClientUser::CloseFiles() { try { STACK_TRACE("p4ClientUser::CloseFiles") Error e; if (NULL != m_BinaryFile) { m_BinaryFile->Close(&e); if (e.Test()) { FormatAndOutputError ("Failed to close binary file:", &e); } delete m_BinaryFile; m_BinaryFile = NULL; } if (NULL != m_TextFile) { m_TextFile->Close(&e); if (e.Test()) { FormatAndOutputError ("Failed to close text file:", &e); } delete m_TextFile; m_TextFile = NULL; } } catch (...) { LogException(_T("p4ClientUser::Final")); } } // Prompt user with error message void p4ClientUser::ErrorPause( char *errBuf, Error *e ) { try { STACK_TRACE("p4ClientUser::ErrorPause") OutputError( errBuf ); MessageBox (NULL, errBuf, _T("p4com"), MB_OK); } catch (...) { LogException(_T("p4ClientUser::ErrorPause")); } } // Writes specified data to the output file. void p4ClientUser::OutputData(FileSys ** OutFile, StrBuf& FileName, enum FileSysType FType, const char *data, int len) { try { STACK_TRACE("p4ClientUser::OutputData") Error e; // If we haven't created a temp output file then do so now! if (NULL == *OutFile) { *OutFile = FileSys::CreateGlobalTemp(FType); (*OutFile)->ClearDeleteOnClose(); // Have had some problems with non-unique filenames. // Delete the file and ignore any errors. (*OutFile)->Unlink(&e); e.Clear(); FileName = (*OutFile)->Name(); (*OutFile)->Open(FOM_WRITE, &e); if (e.Test()) { StrBuf msg; msg = "Error opening temp file for write:"; msg.Append((*OutFile)->Name()); msg.Append(" - "); FormatAndOutputError(msg.Text(), &e); return; } } // Now write the data (*OutFile)->Write(data, len, &e); if (e.Test()) { FormatAndOutputError ("Error writing to temp file :", &e); return; } } catch (...) { LogException(_T("p4ClientUser::OutputData")); } } // OutputData void p4ClientUser::HandleError(Error *e) { try { STACK_TRACE("p4ClientUser::HandleError") StrBuf m; e->Fmt(&m); int s = e->GetSeverity(); // // Empty and informational messages are pushed out as output as nothing // worthy of error handling has occurred. Warnings go into the warnings // list and the rest are lumped together as errors. // if (s == E_EMPTY || s == E_INFO) AddToArray(m.Text(), m.Length(), m_InfoArray); else if (s == E_WARN) AddToArray(m.Text(), m.Length(), m_WarningArray); else AddToArray(m.Text(), m.Length(), m_ErrorArray); } catch (...) { LogException(_T("p4ClientUser::HandleError")); } } // HandleError // Just store the information for later use void p4ClientUser::OutputInfo(char level, const char *data) { try { STACK_TRACE("p4ClientUser::OutputInfo") AddToArray(data, strlen(data), m_InfoArray); } catch (...) { LogException(_T("p4ClientUser::OutputInfo")); } } // Just store the information for later use void p4ClientUser::OutputWarning(const char *data) { try { STACK_TRACE("p4ClientUser::OutputWarning") AddToArray(data, strlen(data), m_WarningArray); } catch (...) { LogException(_T("p4ClientUser::OutputWarning")); } } // Just store the information for later use void p4ClientUser::OutputError(const char *errBuf) { try { STACK_TRACE("p4ClientUser::OutputError") AddToArray(errBuf, strlen(errBuf), m_ErrorArray); } catch (...) { LogException(_T("p4ClientUser::OutputError")); } } // Write the data to a temp file void p4ClientUser::OutputText(const char *data, int len) { try { STACK_TRACE("p4ClientUser::OutputText") OutputData(&m_TextFile, m_TextFileName, FST_TEXT, data, len); } catch (...) { LogException(_T("p4ClientUser::OutputText")); } } // Write the data to a temp file void p4ClientUser::OutputBinary(const char *data, int len) { try { STACK_TRACE("p4ClientUser::OutputBinary") OutputData(&m_BinaryFile, m_BinaryFileName, FST_BINARY, data, len); } catch (...) { LogException(_T("p4ClientUser::OutputBinary")); } } void p4ClientUser::Prompt( const StrPtr &msg, StrBuf &rsp, int noEcho, Error *e ) { try { STACK_TRACE("p4ClientUser::Prompt") rsp.Set(m_input); } catch (...) { LogException(_T("p4ClientUser::Prompt")); } } /* * Diff support for API. Function basically swiped from p4Ruby implementation. * Since the Diff class only writes its output * to files, we run the requested diff putting the output into a temporary * file. Then we read the file in and add its contents line by line to the * results. */ void p4ClientUser::Diff( FileSys *f1, FileSys *f2, int doPage, char *diffFlags, Error *e ) { try { STACK_TRACE("p4ClientUser::Diff") // // Duck binary files. Much the same as ClientUser::Diff, we just // put the output into result space rather than stdout. // if( !f1->IsTextual() || !f2->IsTextual() ) { if ( f1->Compare( f2, e ) ) { char *data = "(... files differ ...)"; AddToArray(data, strlen(data), m_InfoArray); } 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. // :: Diff 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 ) ) { AddToArray(b.Text(), b.Length(), m_InfoArray); } } } delete t; delete f1_bin; delete f2_bin; if ( e->Test() ) HandleError( e ); } catch (...) { LogException(_T("p4ClientUser::OutputText")); } } // Add specified text to specified array void p4ClientUser::AddToArray(const char *data, int len, StrBufArray *sba) { STACK_TRACE("p4ClientUser::AddToArray") sba->Put((void *)data); }; // AddToArray void p4ClientUser::OutputDict(StrDict *dict) { STACK_TRACE("p4ClientUser::OutputDict") StrBuf msg; StrRef var, val; // Dump out the variables, using the GetVar( x ) interface. // Taken from API doc. for( int i = 0; dict->GetVar( i, var, val ); i++ ) { if ( var == "specdef" || var == "func" || var == "specFormatted" ) continue; // otherAction and otherOpen go at level 2, as per 99.1 + earlier msg.Clear(); msg << var << " " << val; char level = strncmp( var.Text(), "other", 5 ) ? '1' : '2'; OutputInfo( level, msg.Text() ); } } void p4ClientUser::OutputStat(StrDict *values) { STACK_TRACE("p4ClientUser::OutputStat") try { StrPtr * spec = values->GetVar( "specdef" ); StrPtr * data = values->GetVar( "data" ); StrPtr * sf = values->GetVar( "specFormatted" ); StrDict * dict = values; Error e; if (spec) { if (m_spec) delete m_spec; if (m_specData) delete m_specData; m_spec = new Spec(spec->Text(), "", &e); m_specData = new SpecDataTable(); if(e.Test()) { HandleError(&e); return; } } if (data && spec) { // 2000.1 -> 2005.1 server's handle tagged form output by supplying the form // as text in the 'data' variable. We need to convert it to a dictionary // using the supplied spec. // Parse the form. Use the ParseNoValid() interface to prevent // errors caused by the use of invalid defaults for select items in // jobspecs. m_spec->ParseNoValid(data->Text(), m_specData, &e); if(e.Test()) { HandleError(&e); return; } dict = m_specData->Dict(); } else if (sf) { // Already formatted by server - just save a copy m_specData->Dict()->CopyVars(*dict); } OutputDict(dict); } catch (...) { LogException(_T("p4ClientUser::OutputStat")); } } void p4ClientUser::InputData( StrBuf *strbuf, Error *e ) { try { STACK_TRACE("p4ClientUser::InputData") StrBuf specText; m_spec->Format(m_specData, &specText); strbuf->Set(specText.Text()); } catch (...) { LogException(_T("p4ClientUser::InputData")); } } BSTR p4ClientUser::GetVar(LPCSTR var) { try { STACK_TRACE("p4ClientUser::GetVar get") CComBSTR value; if (m_specData) { StrPtr *sp = m_specData->Dict()->GetVar(var); if (sp != NULL) { StrBuf sb; sb.Set(sp->Text()); value = TranslateToBSTR(&sb); } } return value.Detach(); } catch (...) { LogException(_T("p4ClientUser::GetVar")); return NULL; } } int p4ClientUser::VarExists(LPCSTR var) { try { STACK_TRACE("p4ClientUser::VarExists") if (m_specData) { StrPtr *sp = m_specData->Dict()->GetVar(var); if (sp != NULL) { return 1; } } return 0; } catch (...) { LogException(_T("p4ClientUser::VarExists")); return NULL; } } void p4ClientUser::SetVar(LPCSTR varName, BSTR newVal) { try { STACK_TRACE("p4ClientUser::SetVar") if (m_specData) { m_specData->Dict()->ReplaceVar(varName, TranslateFromBSTR(newVal)); } } catch (...) { LogException(_T("p4ClientUser::SetVar")); } } void p4ClientUser::SetArrayVar(LPCSTR varName, StrBufArray *newVal) { int i; char idx[32]; char * var; StrBuf s; STACK_TRACE("p4ClientUser::SetArrayVar") if (!m_specData) return; var = (char *)idx; char * tag = (char *)varName; // Remove all files in the spec list and replace with new set for (i = 0; ; i++) { wsprintfA(idx, "%s%d", tag, i); if (!m_specData->Dict()->GetVar(var)) break; m_specData->Dict()->RemoveVar(var); } for (i = 0; i < newVal->Count(); i++) { wsprintfA(idx, "%s%d", tag, i); s = (LPCSTR)newVal->Get(i); m_specData->Dict()->SetVar(idx, s); } } StrBufArray *p4ClientUser::GetArrayVar(LPCSTR varName) { StrBufArray *sba = new StrBufArray(); int i; char idx[32]; char * var; StrPtr *field; STACK_TRACE("p4ClientUser::GetArrayVar") if (m_specData) { var = (char *) idx; char *tag = (char *)varName; for ( i = 0; ; i++ ) { wsprintfA( idx, "%s%d", tag, i ); if ( field = m_specData->Dict()->GetVar( var ) ) { sba->Put(field->Text()); } else break; } } return sba; }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 11314 | Robert Cowham | Initial population of perforce_software version of P4OFC | ||
//guest/robert_cowham/perforce/API/p4com/main/src/p4clientuser.cpp | |||||
#10 | 9835 | Robert Cowham | Added SetProtocolV | ||
#9 | 7548 | Robert Cowham | - Fix backwards compatibility problem with P4OFC - which uses setprotocol "API", "57" | ||
#8 | 7443 | Robert Cowham | Update for new API outputstat. | ||
#7 | 6050 | Robert Cowham |
- Use P4API 2007.2 - Added SetProg, SetVersion, SetProtocol and GetProtocol |
||
#6 | 5194 | Robert Cowham |
Changes (2005.1.0.0) Added Dropped() to detect dropped connections Added Input property to provide input e.g. for passwords Added FormatDateTime() as a convenience for calling C Runtime function (for VB use) Imported changes from p4ofc which made it more reliable. |
||
#5 | 4242 | Robert Cowham |
Fixed some String leaks. Fixed problem with writing to temporary files. Updated p4API to released version of 2003.2. Start of Delphi example. |
||
#4 | 4182 | Robert Cowham | Release 1.0.0.4 - put output of Diff in normal output array. | ||
#3 | 3614 | Robert Cowham | Fixed memory leak and hanging over of variables when using forms. | ||
#2 | 3612 | Robert Cowham |
Version 1.0.0.2 New interface for ServerVersion. New put property ArrayVarVar which is the same as ArrayVar but takes an Array of Variants rather than strings (for Lisp compatibility!). |
||
#1 | 3525 | Robert Cowham | Initial release |