/* Copyright (C) 2007 Vizim Worldwide. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. */ /******************************************************************************* * Name: p4ClientUser.cpp * * Author: Robert Cowham <robert@vizim.com> * * Description: * Wrapper for Perforce ClientUser class * ******************************************************************************/ #include "stdafx.h" #include "p4ClientUser.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// p4ClientUser::p4ClientUser() : m_BinaryFile(NULL), m_TextFile(NULL), m_spec(NULL), m_specData(NULL), m_ErrorLogger(NULL), m_InfoArray(new StrArray()), m_WarningArray(new StrArray()), m_ErrorArray(new StrArray()) { } void p4ClientUser::Reset() { // Ensure our arrays are OK. delete m_InfoArray; delete m_WarningArray; delete m_ErrorArray; m_InfoArray = new StrArray(); m_WarningArray = new StrArray(); m_ErrorArray = new StrArray(); CloseFiles(); } p4ClientUser::~p4ClientUser() { if (m_spec) delete m_spec; if (m_specData) delete m_specData; if (m_InfoArray) delete m_InfoArray; if (m_ErrorArray) delete m_ErrorArray; if (m_WarningArray) delete m_WarningArray; CloseFiles(); } // Format error for printing for user void p4ClientUser::FormatAndOutputError(LPCSTR m, Error *e) { // Callback for error logger if (m_ErrorLogger) { StrBuf err; StrBuf msg = m; e->Fmt(&err); msg.Append(err.Text()); (*m_ErrorLogger) (msg.Text()); } } void 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; } } // Prompt user with error message void p4ClientUser::ErrorPause( char *errBuf, Error *e ) { OutputError( errBuf ); MessageBox (NULL, (LPTSTR) errBuf, _T("p4api"), MB_OK); } // Writes specified data to the output file. void p4ClientUser::OutputData(FileSys ** OutFile, StrBuf& FileName, enum FileSysType FType, const char *data, int len) { 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; } } // OutputData void p4ClientUser::HandleError(Error *e) { 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_InfoArray); else if (s == E_WARN) AddToArray(m.Text(), m_WarningArray); else AddToArray(m.Text(), m_ErrorArray); } // HandleError // Just store the information for later use void p4ClientUser::OutputInfo(char level, const char *data) { AddToArray(data, m_InfoArray); } // Just store the information for later use void p4ClientUser::OutputWarning(const char *data) { AddToArray(data, m_WarningArray); } // Just store the information for later use void p4ClientUser::OutputError(const char *errBuf) { AddToArray(errBuf, m_ErrorArray); } // Write the data to a temp file void p4ClientUser::OutputText(const char *data, int len) { OutputData(&m_TextFile, m_TextFileName, FST_TEXT, data, len); } // Write the data to a temp file void p4ClientUser::OutputBinary(const char *data, int len) { OutputData(&m_BinaryFile, m_BinaryFileName, FST_BINARY, data, len); } void p4ClientUser::Prompt( const StrPtr &msg, StrBuf &rsp, int noEcho, Error *e ) { rsp.Set(m_input); } /* * 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 ) { // 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, 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(), m_InfoArray); } } } delete t; delete f1_bin; delete f2_bin; if ( e->Test() ) HandleError( e ); } // Add specified text to specified array void p4ClientUser::AddToArray(const char *data, StrArray *sba) { sba->Put()->Set(data); } void p4ClientUser::OutputDict(StrDict *dict) { 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) { 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); if(e.Test()) { HandleError(&e); return; } } if (data) { // 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. Spec s( spec->Text(), "", &e ); if( !e.Test() ) s.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 = new SpecDataTable(); m_specData->Dict()->CopyVars(*dict); } OutputDict(dict); } void p4ClientUser::InputData( StrBuf *strbuf, Error *e ) { StrBuf specText; m_spec->Format(m_specData, &specText); strbuf->Set(specText.Text()); } StrPtr *p4ClientUser::GetVar(LPCSTR var) { if (m_specData) { return m_specData->Dict()->GetVar(var); } return NULL; } void p4ClientUser::SetVar(LPCSTR varName, LPCSTR newVal) { if (m_specData) { m_specData->Dict()->ReplaceVar(varName, newVal); } } void p4ClientUser::SetArrayVar(LPCSTR varName, StrArray *newVal) { int i; char idx[32]; char * var; StrBuf s; 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.Set(newVal->Get(i)); m_specData->Dict()->SetVar(idx, s); } } StrArray *p4ClientUser::GetArrayVar(LPCSTR varName) { StrArray *sba = new StrArray(); int i; char idx[32]; char * var; StrPtr *field; 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()->Set(field->Text()); } else break; } } return sba; }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#2 | 7502 | Robert Cowham |
- Updated for 2009.1 API - use static version (/MT rather than /MD) - Fixed SetProg/SetVersion calls - ensures they are called at correct moment. - Add SetProtocol - Added some more tests/examples |
||
#1 | 6468 | Robert Cowham | Draft version. |