// PerforceClientService.cpp : Defines the class behaviors for the application. // #include "stdafx.h" #include "PerforceClientService.h" #include "PerforceClientServiceDlg.h" #include "FileSystemEvent.h" #include "p4api/clientapi.h" #include <afxmt.h> #include <assert.h> #include <stdio.h> #include <deque> #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif namespace CPerforceClientServiceAppNamespace { typedef std::deque<FileSystemEvent *>(FileSystemEvents); enum FileAction { FA_add, FA_modify, FA_delete, FA_count }; char const * ms_fileActionTable[FA_count] = { "FA_add", "FA_modify", "FA_delete", }; enum PerforceState { PS_unknown, PS_unchanged, PS_add, PS_edit, PS_delete, PS_count }; char const * ms_perforceStateTable[PS_count] = { "PS_unknown", "PS_unchanged", "PS_add", "PS_edit", "PS_delete", }; class MyClientUser : public ClientUser { public: MyClientUser(FileAction fileAction, char const * fileName); virtual void Message( Error *err ); virtual void OutputError( const_char *errBuf ); virtual void OutputInfo( char level, const_char *data ); virtual void OutputBinary( const_char *data, int length ); virtual void OutputText( const_char *data, int length ); virtual void OutputStat( StrDict *varList ); virtual FileSys * File(FileSysType type); void UpdatePerforce(); private: void actionIgnore(); void actionAdd(); void actionEdit(); void actionDelete(); void actionRevert(); void actionRevertUnchanged(); void actionRevertEdit(); void actionRevertDelete(); private: typedef void (MyClientUser::*Action)(); static const Action ms_actionTable[FA_count][PS_count]; private: FileAction m_fileAction; char const * m_fileName; PerforceState m_perforceState; }; class MyFileSys : public FileSys { public: MyFileSys(FileSysType type); virtual ~MyFileSys(); virtual void Set( const StrPtr &name ); virtual int DoIndirectWrites(); virtual void Translator( CharSetCvt * ); virtual void Open( FileOpenMode mode, Error *e ); virtual void Write( const char *buf, int len, Error *e ); virtual int Read( char *buf, int len, Error *e ); virtual void Close( Error *e ); virtual int Stat(); virtual int StatModTime(); virtual void Truncate( Error *e ); virtual void Unlink( Error *e ); virtual void Rename( FileSys *target, Error *e ); virtual void Chmod( FilePerm perms, Error *e ); virtual void ChmodTime( Error *e ); virtual int GetFd(); virtual size_t GetSize(); virtual void Seek( off_t offset ); private: FileSys * m_fileSys; bool m_writing; }; HANDLE ms_semaphore; CRITICAL_SECTION ms_criticalSection; FileSystemEvents ms_fileSystemEvents; ClientApi ms_clientApi; const MyClientUser::Action MyClientUser::ms_actionTable[FA_count][PS_count] = { { /* add, unknown */ &actionAdd, // PASS five /* add, unchanged */ &actionEdit, // PASS, PASS (original, different) one /* add, add */ &actionIgnore, // PASS four /* add, edit */ &actionRevertUnchanged, // PASS, PASS (original, different) two /* add, delete */ &actionRevertEdit, // three }, { /* modify, unknown */ &actionAdd, /* modify, unchanged */ &actionEdit, /* modify, add */ &actionIgnore, /* modify, edit */ &actionRevertUnchanged, /* modify, delete */ &actionRevertEdit, }, { /* delete, unknown */ &actionIgnore, /* delete, unchanged */ &actionDelete, /* delete, add */ &actionRevert, /* delete, edit */ &actionRevertDelete, /* delete, delete */ &actionIgnore, }, }; } using namespace CPerforceClientServiceAppNamespace; #define snprintf _snprintf MyClientUser::MyClientUser(FileAction fileAction, char const * fileName) : ClientUser(), m_fileAction(fileAction), m_fileName(fileName), m_perforceState(PS_unchanged) { } void MyClientUser::Message(Error * err) { // handle fstat being called on a file that perforce knows nothing about m_perforceState = PS_unknown; StrBuf msg; err->Fmt(&msg); char buffer[2048]; snprintf(buffer, sizeof(buffer), "ClientUser::Message %s\n", msg.Text()); CPerforceClientServiceDlg::Log("%s", msg.Text()); } void MyClientUser::OutputError(const_char *errBuf) { CPerforceClientServiceDlg::Log("%s", errBuf); } void MyClientUser::OutputInfo(char level, const_char *data) { CPerforceClientServiceDlg::Log("%d %s", level, data); } void MyClientUser::OutputBinary(const_char *data, int length) { char buffer[2048]; snprintf(buffer, sizeof(buffer), "ClientUser::OutputBinary %d\n", length); OutputDebugString(buffer); } void MyClientUser::OutputText(const_char *data, int length) { char buffer[2048]; snprintf(buffer, sizeof(buffer), "ClientUser::OutputText %d\n", length); OutputDebugString(buffer); } void MyClientUser::OutputStat(StrDict *varList) { char buffer[2048]; snprintf(buffer, sizeof(buffer), "ClientUser::OutputStat\n"); OutputDebugString(buffer); StrRef var; StrRef val; for (int i = 0; varList->GetVar(i, var, val); ++i) { if (strcmp(var.Text(), "action") == 0) { if (strcmp(val.Text(), "edit") == 0) m_perforceState = PS_edit; else if (strcmp(val.Text(), "add") == 0) m_perforceState = PS_add; else if (strcmp(val.Text(), "delete") == 0) m_perforceState = PS_delete; } snprintf(buffer, sizeof(buffer), " %s=%s\n", var.Text(), val.Text()); OutputDebugString(buffer); } } FileSys * MyClientUser::File(FileSysType type) { return new MyFileSys(type); } void MyClientUser::UpdatePerforce() { // run fstat to see what the file in perforce is like { char * arguments[] = { const_cast<char *>(m_fileName) }; ms_clientApi.SetArgv(sizeof(arguments) / sizeof(arguments[0]), arguments); ms_clientApi.Run("fstat", this); } char buffer[2048]; snprintf(buffer, sizeof(buffer), " %s %s %s\n", m_fileName, ms_fileActionTable[m_fileAction], ms_perforceStateTable[m_perforceState]); OutputDebugString(buffer); Action action = ms_actionTable[m_fileAction][m_perforceState]; (this->*action)(); OutputDebugString("\n\n"); } void MyClientUser::actionIgnore() { OutputDebugString("actionIgnore\n"); } void MyClientUser::actionAdd() { OutputDebugString("actionAdd\n"); char * arguments[] = { const_cast<char *>(m_fileName) }; ms_clientApi.SetArgv(sizeof(arguments) / sizeof(arguments[0]), arguments); ms_clientApi.Run("add", this); } void MyClientUser::actionEdit() { OutputDebugString("actionEdit\n"); char * arguments[] = { const_cast<char *>(m_fileName) }; ms_clientApi.SetArgv(sizeof(arguments) / sizeof(arguments[0]), arguments); ms_clientApi.Run("edit", this); actionRevertUnchanged(); } void MyClientUser::actionRevert() { OutputDebugString("actionRevert\n"); char * arguments[] = { const_cast<char *>(m_fileName) }; ms_clientApi.SetArgv(sizeof(arguments) / sizeof(arguments[0]), arguments); ms_clientApi.Run("revert", this); } void MyClientUser::actionRevertUnchanged() { OutputDebugString("actionRevertUnchanged\n"); char * arguments[] = { "-a", const_cast<char *>(m_fileName) }; ms_clientApi.SetArgv(sizeof(arguments) / sizeof(arguments[0]), arguments); ms_clientApi.Run("revert", this); } void MyClientUser::actionRevertEdit() { OutputDebugString("actionRevertEdit\n"); actionRevert(); actionEdit(); } void MyClientUser::actionDelete() { OutputDebugString("actionDelete\n"); char * arguments[] = { const_cast<char *>(m_fileName) }; ms_clientApi.SetArgv(sizeof(arguments) / sizeof(arguments[0]), arguments); ms_clientApi.Run("delete", this); } void MyClientUser::actionRevertDelete() { OutputDebugString("actionRevertDelete\n"); actionRevert(); actionDelete(); } ///////////////////////////////////////////////////////////////////////////// MyFileSys::MyFileSys(FileSysType type) : FileSys(), m_fileSys(FileSys::Create(type)), m_writing(false) { } MyFileSys::~MyFileSys() { delete m_fileSys; } void MyFileSys::Set( const StrPtr &name ) { FileSys::Set(name); m_fileSys->Set(name); } int MyFileSys::DoIndirectWrites() { return m_fileSys->DoIndirectWrites(); } void MyFileSys::Translator( CharSetCvt * arg) { FileSys::Translator(arg); m_fileSys->Translator(arg); } void MyFileSys::Open( FileOpenMode mode, Error *e ) { if (mode == FOM_WRITE) { m_writing = true; return; } m_fileSys->Open(mode, e); } void MyFileSys::Write( const char *buf, int len, Error *e ) { // m_fileSys->Write(buf, len, e); } int MyFileSys::Read( char *buf, int len, Error *e ) { return m_fileSys->Read(buf, len, e); } void MyFileSys::Close( Error *e ) { if (m_writing) { m_writing = false; return; } m_fileSys->Close(e); } int MyFileSys::Stat() { return m_fileSys->Stat(); } int MyFileSys::StatModTime() { return m_fileSys->StatModTime(); } void MyFileSys::Truncate( Error *e ) { // m_fileSys->Truncate(e); } void MyFileSys::Unlink( Error *e = 0 ) { // m_fileSys->Unlink(e); } void MyFileSys::Rename( FileSys *target, Error *e ) { // m_fileSys->Rename(target, e); } void MyFileSys::Chmod( FilePerm perms, Error *e ) { m_fileSys->Chmod(perms, e); } void MyFileSys::ChmodTime( Error *e ) { m_fileSys->ChmodTime(e); } int MyFileSys::GetFd() { return m_fileSys->GetFd(); } size_t MyFileSys::GetSize() { return m_fileSys->GetSize(); } void MyFileSys::Seek(off_t offset) { m_fileSys->Seek(offset); } ///////////////////////////////////////////////////////////////////////////// // CPerforceClientServiceApp BEGIN_MESSAGE_MAP(CPerforceClientServiceApp, CWinApp) //{{AFX_MSG_MAP(CPerforceClientServiceApp) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG ON_COMMAND(ID_HELP, CWinApp::OnHelp) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CPerforceClientServiceApp construction CPerforceClientServiceApp::CPerforceClientServiceApp() { // TODO: add construction code here, // Place all significant initialization in InitInstance } ///////////////////////////////////////////////////////////////////////////// // The one and only CPerforceClientServiceApp object CPerforceClientServiceApp theApp; ///////////////////////////////////////////////////////////////////////////// // CPerforceClientServiceApp initialization BOOL CPerforceClientServiceApp::InitInstance() { // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need. #ifdef _AFXDLL Enable3dControls(); // Call this when using MFC in a shared DLL #else Enable3dControlsStatic(); // Call this when linking to MFC statically #endif // Bring up the connection { ms_clientApi.SetPort("1666"); Error e; ms_clientApi.Init(&e); if (e.Test()) { StrBuf msg; e.Fmt(&msg); MessageBox(NULL, msg.Text(), "P4 error", MB_ICONSTOP); return FALSE; } } ms_clientApi.SetProg("PerforceClientService"); // Run the command // ms_clientApi.SetArgv( 0, NULL); // ms_clientApi.Run("info", &ui); InitializeCriticalSection(&ms_criticalSection); ms_semaphore = CreateSemaphore(NULL, 0, INT_MAX, NULL); assert(ms_semaphore != NULL); CWinThread * thread = AfxBeginThread(threadProc, NULL); CPerforceClientServiceDlg dlg; m_pMainWnd = &dlg; int nResponse = dlg.DoModal(); if (nResponse == IDOK) { // TODO: Place code here to handle when the dialog is // dismissed with OK } else if (nResponse == IDCANCEL) { // TODO: Place code here to handle when the dialog is // dismissed with Cancel } // Signal the semaphore again to let the thread die ReleaseSemaphore(ms_semaphore, 1, NULL); WaitForSingleObject(thread->m_hThread, INFINITE); // Close connection { Error e; ms_clientApi.Final(&e); if (e.Test()) { StrBuf msg; e.Fmt(&msg); MessageBox(NULL, msg.Text(), "P4 error", MB_ICONSTOP); return 1; } } // Since the dialog has been closed, return FALSE so that we exit the // application, rather than start the application's message pump. return FALSE; } void CPerforceClientServiceApp::enqueueFileSystemEvent(FileSystemEvent * event) { EnterCriticalSection(&ms_criticalSection); ms_fileSystemEvents.push_back(event); LeaveCriticalSection(&ms_criticalSection); ReleaseSemaphore(ms_semaphore, 1, NULL); } UINT CPerforceClientServiceApp::threadProc(LPVOID) { for (;;) { DWORD result = WaitForSingleObject(ms_semaphore, INFINITE); assert(result == WAIT_OBJECT_0); FileSystemEvent * event = NULL; EnterCriticalSection(&ms_criticalSection); if (!ms_fileSystemEvents.empty()) { event = ms_fileSystemEvents.front(); ms_fileSystemEvents.pop_front(); } LeaveCriticalSection(&ms_criticalSection); if (!event) return 0; switch (event->m_type) { case FileSystemEvent::T_add: addFile(event->m_path); break; case FileSystemEvent::T_delete: deleteFile(event->m_path); break; case FileSystemEvent::T_rename: renameFile(event->m_path, event->m_newPath); break; case FileSystemEvent::T_modify: modifyFile(event->m_path); break; } delete event; } } bool isTmpFile(char const * fileName) { char const * dot = strrchr(fileName, '.'); return dot && stricmp(dot, ".tmp") == 0; } void CPerforceClientServiceApp::addFile(char const * fileName) { if (!isTmpFile(fileName)) { MyClientUser myClientUser(FA_add, fileName); myClientUser.UpdatePerforce(); } } void CPerforceClientServiceApp::deleteFile(char const * fileName) { if (!isTmpFile(fileName)) { MyClientUser myClientUser(FA_delete, fileName); myClientUser.UpdatePerforce(); } } void CPerforceClientServiceApp::modifyFile(char const * fileName) { if (!isTmpFile(fileName)) { MyClientUser myClientUser(FA_modify, fileName); myClientUser.UpdatePerforce(); } } void CPerforceClientServiceApp::renameFile(char const * oldFileName, char const * newFileName) { CPerforceClientServiceDlg::Log("*** Rename %s to %s\n", oldFileName, newFileName); }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#5 | 7201 | Jeff Grills | Upgrade to VS2005 and the 2008.2 p4api | ||
#4 | 4963 | Jeff Grills |
Major clean up and code reorganization. Add new dialog to control the directories that are watched. Persist the watched directories in the windows registry. Restore the watched directories when the application is run again. Handle login correctly. Change the way error messages are examined to make them significantly more robust. Issue an "info" command at startup to make sure we can communicate with the server and get logged in. Change the icon to the standard P4 icon. Allow the dialog to be minimized to the systray. Clicking on the systray icon will show the dialog again. |
||
#3 | 4962 | Jeff Grills |
Remove comments that indicated current test status. Hook up renaming so that syncing works (but is ugly) |
||
#2 | 4961 | Jeff Grills |
Add the p4 headers to the DSP so that WorkspaceWhiz can open them, Rework the perforce update engine to be significantly more simple. Add MyFileSys class that prevents any disk updates when doing reverts. |
||
#1 | 4896 | Jeff Grills |
First drop of the PerforceClientService. This version is only barely functional, supporting adds, edits, and deletes. It does not handle multiple operations on the same file gracefully. |