// VerEngine.cpp: implementation of the CVerEngine class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "VerEngine.h" #include "ssauterr.h" #include "Error.h" #include ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CVerEngine::CVerEngine() { } CVerEngine::~CVerEngine() { } HRESULT CVerEngine::NewInit(LPCTSTR szVSSRootPrj) { // save the root prj m_szVSSRootPrj = szVSSRootPrj; HRESULT hr = E_FAIL; // check if we already have a db instance if(!m_pIDB) { // create db instance hr = CoCreateInstance(CLSID_VSSDatabase, NULL, CLSCTX_INPROC_SERVER, IID_IVSSDatabase, (void**)&m_pIDB); if(FAILED(hr)) return hr; } // Open the database hr = m_pIDB->Open(m_bstrSrcSafeIni,m_bstrUsername,m_bstrPassword); if(FAILED(hr)) return hr; return hr; } HRESULT CVerEngine::ShutDown() { // release of interface ptr here and not during destructor, // since CVerEngine could live on stack in the same frame that CoUninitialize() is called, // i.e. CoUninitialize() would be called before the destructor calls release and gets // an Access Violation. m_pIDB.Release(); return S_OK; } HRESULT CVerEngine::AddPrj(LPCTSTR szBasePrj,LPCTSTR szRelSpec) { _ASSERT(szBasePrj && szRelSpec); HRESULT hr = S_OK; CComPtr pIItem; wstring szPrj(szBasePrj); MakePrjSpec(szPrj,szRelSpec); // see if the item exists CError::Trace(szPrj.c_str()); CError::Trace(" Add "); hr = GetPrjEx(szPrj.c_str(),&pIItem,true); if( SUCCEEDED(hr) ) { if(hr == S_FALSE) CError::Trace("created "); } else FAIL_RTN1(hr,"\nGetPrjEx"); CError::Trace("\n"); return hr; } HRESULT CVerEngine::RenamePrj(LPCTSTR szBasePrj,LPCTSTR szRelSpec,LPCTSTR szRelSpecOld) { _ASSERTE(szBasePrj && szRelSpec && szRelSpecOld); HRESULT hr; CComPtr pIItem; wstring szItem(szBasePrj); MakePrjSpec(szItem,szRelSpecOld); // see if the item exists CError::Trace(szRelSpecOld); CError::Trace(" Rename to "); CError::Trace(szRelSpec); hr = GetPrjEx(szItem.c_str(),&pIItem,true); if(SUCCEEDED(hr)) { wstring szFileName(szRelSpec); int iFileNameIndex = szFileName.find_last_of(L"\\/"); if(iFileNameIndex == wstring::npos) iFileNameIndex = 0; else iFileNameIndex++; hr = pIItem->put_Name(_bstr_t(szFileName.substr(iFileNameIndex).c_str())); IF_FAIL_RTN1(hr,"\nput_Name"); } else FAIL_RTN1(hr,"\nGetPrjEx"); CError::Trace("\n"); return hr; } HRESULT CVerEngine::Rename(LPCTSTR szBasePrj,LPCTSTR szDir,LPCTSTR szRelSpec,LPCTSTR szRelSpecOld) { _ASSERTE(szBasePrj && szRelSpec && szRelSpecOld); HRESULT hr; CComPtr pIItem; wstring szOldItem(szBasePrj); MakePrjSpec(szOldItem,szRelSpecOld); // see if the item exists CError::Trace(szRelSpecOld); CError::Trace(" Rename to "); CError::Trace(szRelSpec); hr = GetItemEx(szOldItem.c_str(),&pIItem,true); if(SUCCEEDED(hr)) { if(hr == S_FALSE) { CError::Trace(" created "); // file was created, therefore let's checkin the old version _ASSERTE(szDir); wstring szFileSpec(szDir); szFileSpec.append(L"\\").append(szRelSpec); hr = Sync2(szBasePrj,szRelSpecOld,szFileSpec.c_str()); IF_FAIL_RTN1(hr,"\nSync"); } wstring szItem(szRelSpec); int iFileNameIndex = szItem.find_last_of(L"\\/"); if(iFileNameIndex == wstring::npos) iFileNameIndex = 0; else iFileNameIndex++; CComBSTR bstrFileName(szItem.substr(iFileNameIndex).c_str()); hr = pIItem->put_Name(bstrFileName); IF_FAIL_RTN1(hr,"\nput_Name"); } else FAIL_RTN1(hr,"\nGetItemEx"); CError::Trace("\n"); return hr; } HRESULT CVerEngine::Sync2(LPCTSTR szPrj,LPCTSTR szFileName,LPCTSTR szFileSpec) { // return Sync(szPrj,NULL,szFileName,szFileSpec); // @todo: handle errors HRESULT hr; CComPtr pIItem; wstring szItem(szPrj); MakePrjSpec(szItem,szFileName); // complete file/prj specs wstring szFSpec; szFSpec = szFileSpec; // see if the item exists CError::Trace(szItem.c_str()); CError::Trace(" Sync "); hr = GetItemEx(szItem.c_str(),&pIItem,true); if(SUCCEEDED(hr)) { hr = CheckIn(pIItem,szFSpec.c_str()); if(hr == ESS_FILE_SHARE) { // File %s is already open, meaning is held open by other process // Let's hope they close the file and we can try to add it agian, // so let's ignore it for now CError::Trace("not checked in(isopen)\n"); return S_FALSE; } else IF_FAIL_RTN1(hr,"\nCheckin"); CError::Trace("synced "); } else FAIL_RTN1(hr,"\nget_VSSItem"); CError::Trace("\n"); return hr; } HRESULT CVerEngine::Sync(LPCTSTR szBasePrj,LPCTSTR szDir,LPCTSTR szRelSpec,LPCTSTR szFileSpec) { // @todo: handle errors _ASSERT(m_pIDB && szBasePrj && szRelSpec); _ASSERTE(szDir||szFileSpec); HRESULT hr; CComPtr pIItem; wstring szItem(szBasePrj); MakePrjSpec(szItem,szRelSpec); // complete file/prj specs wstring szFSpec; if(szDir) { szFSpec = szDir; szFSpec.append(L"\\").append(szRelSpec); } else { _ASSERTE(szFileSpec); szFSpec = szFileSpec; } // see if the item exists CError::Trace(szRelSpec); CError::Trace(" Sync "); hr = GetItemEx(szItem.c_str(),&pIItem,false); if(SUCCEEDED(hr)) { hr = CheckIn(pIItem,szFSpec.c_str()); if(hr == ESS_FILE_SHARE) { // File %s is already open, meaning is held open by other process // Let's hope they close the file and we can try to add it agian, // so let's ignore it for now CError::Trace("not checked in(isopen)\n"); return S_FALSE; } else IF_FAIL_RTN1(hr,"\nCheckin"); CError::Trace("synced "); } else if(hr == ESS_VS_NOT_FOUND) { hr = Add(szItem.c_str(),szFSpec.c_str()); if(hr == ESS_FILE_SHARE) { // File %s is already open, meaning is held open by other process // Let's hope they close the file and we can try to add it agian, // so let's ignore it for now CError::Trace("not added(isopen)\n"); return S_FALSE; } else IF_FAIL_RTN1(hr,"\nAdd"); CError::Trace("added "); } else FAIL_RTN1(hr,"\nget_VSSItem"); CError::Trace("\n"); return hr; } HRESULT CVerEngine::Delete(LPCTSTR szBasePrj,LPCTSTR szRelSpec) { _ASSERT(m_pIDB && szBasePrj && szRelSpec); HRESULT hr = S_OK; CComPtr pIItem; wstring szItem(szBasePrj); MakePrjSpec(szItem,szRelSpec); // see if the item exists CError::Trace(szItem.c_str()); CError::Trace(" Delete "); hr = GetItemEx(szItem.c_str(),&pIItem,false); if( SUCCEEDED(hr) ) { CError::Trace("exists "); // delete the file hr = pIItem->put_Deleted(true); IF_FAIL_RTN1(hr,"\nput_Delete"); CError::Trace("deleted "); } else if( hr == ESS_VS_NOT_FOUND ) { CError::Trace("not-exist "); // This is bad. The file should have been in version control. // We can't add the file and delete it from VSS since the file // might no longer exist. We could create an empty dummy file, // but that's more confusing than helpfull. // Let's just log this error // @todo: log condition that file doesn't exist in VSS hr = S_OK; } else // This is really bad. There is some other error. Maybe we should try and // shutdown the srcsafe db and start it up again (this is slooowww!!!) // or maybe just write the failure to the log FAIL_RTN1(hr,"\nGetItemEx"); CError::Trace("\n"); return hr; } void CVerEngine::MakePrjSpec(wstring &szDest,LPCTSTR szSource) { // szDest = m_szVSSRootPrj + [/] if(m_szVSSRootPrj[m_szVSSRootPrj.length()-1] != L'/' && szDest[0] != L'/') szDest.insert(0,L"/"); szDest.insert(0,m_szVSSRootPrj.c_str()); // szDest = szDest + [/] + szSource if(szDest[szDest.length()-1] != L'/' && szSource[0] != L'/') szDest.append(L"/"); szDest.append(szSource); // convert all backslashes with slashes int pos = 0; while((pos = szDest.find(L'\\',pos)) != wstring::npos) { szDest[pos] = L'/'; pos++; } } HRESULT CVerEngine::Add(LPCTSTR szItem,LPCTSTR szFileSpec) { _ASSERTE(szItem && szFileSpec); HRESULT hr = S_OK; CComPtr pIPrj; CComPtr pIItem; // get prj wstring szTmp = szItem; int iFileNameIndex = szTmp.find_last_of(L"/"); if(iFileNameIndex == wstring::npos) return E_FAIL; hr = GetPrjEx(szTmp.substr(0,iFileNameIndex).c_str(),&pIPrj,true); IF_FAIL_RTN1(hr,"GetPrjEx"); CComBSTR bstrFileSpec(szFileSpec); hr = pIPrj->Add(bstrFileSpec,NULL,VSSFLAG_USERRONO|VSSFLAG_GETNO,&pIItem); // VSSFLAG_KEEPYES if(hr == 0x80040000) // @todo tmp fix, since pIPrj->Add has a bug when called with VSSFLAG_KEEPYES hr = S_OK; IF_FAIL_RTN1(hr,"Add"); return hr; } HRESULT CVerEngine::GetLocalWritable(LPCTSTR szFileSpec,LPCTSTR szBasePrj,LPCTSTR szRelSpec) { _ASSERTE(m_pIDB && szFileSpec && szBasePrj && szRelSpec); HRESULT hr = S_OK; CComPtr pIItem; wstring szItem(szBasePrj); MakePrjSpec(szItem,szRelSpec); // see if the item exists CError::Trace(szBasePrj); CError::Trace(L"/"); CError::Trace(szRelSpec); CError::Trace(" Get "); hr = GetItemEx(szItem.c_str(),&pIItem,false); if(SUCCEEDED(hr)) { CError::Trace("exists "); // checkout file CComBSTR bstrFileSpec(szFileSpec); hr = pIItem->Get(&bstrFileSpec,VSSFLAG_REPREPLACE|VSSFLAG_USERRONO); IF_FAIL_RTN1(hr,"\nGet"); CError::Trace("gotten "); } else if(hr == ESS_VS_NOT_FOUND) { HANDLE hFile = NULL; hFile = CreateFile(szFileSpec, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_SEQUENTIAL_SCAN, NULL); if(hFile == INVALID_HANDLE_VALUE) { hFile = NULL; hr = GetLastError(); FAIL_RTN1(hr,"\nCreateFile"); } CloseHandle(hFile); hFile = NULL; hr = S_OK; } CError::Trace("\n"); return hr; } HRESULT CVerEngine::CheckOut(LPCTSTR szFileSpec,LPCTSTR szBasePrj,LPCTSTR szRelSpec) { _ASSERTE(m_pIDB && szFileSpec && szBasePrj && szRelSpec); HRESULT hr = S_OK; CComPtr pIItem; wstring szItem(szBasePrj); MakePrjSpec(szItem,szRelSpec); // see if the item exists CError::Trace(szBasePrj); CError::Trace(L"/"); CError::Trace(szRelSpec); CError::Trace(" Checkout "); hr = GetItemEx(szItem.c_str(),&pIItem,true); if( SUCCEEDED(hr) ) { CError::Trace("exists "); // checkout file hr = CheckOutLocal(pIItem,szFileSpec); IF_FAIL_RTN1(hr,"\nCheckout"); CError::Trace("gotten "); } else FAIL_RTN1(hr,"\nGetItemEx"); CError::Trace("\n"); return hr; } HRESULT CVerEngine::CheckOutNoGet(IVSSItem *pIItem) { _ASSERTE(pIItem); HRESULT hr = S_OK; long iStatus = 0; // is files checked out? hr = pIItem->get_IsCheckedOut(&iStatus); IF_FAIL_RTN1(hr,"\nget_IsCheckOut"); // check it out to me if(iStatus != VSSFILE_CHECKEDOUT_ME) { hr = pIItem->Checkout(NULL,NULL,VSSFLAG_GETNO); IF_FAIL_RTN1(hr,"\nCheckout"); } return hr; } HRESULT CVerEngine::CheckIn(IVSSItem *pIItem,LPCTSTR szFileSpec) { _ASSERTE(pIItem && szFileSpec); HRESULT hr = S_OK; hr = CheckOutNoGet(pIItem); if(FAILED(hr)) return hr; // checkin hr = pIItem->Checkin(NULL,_bstr_t(szFileSpec),VSSFLAG_KEEPYES); return hr; } HRESULT CVerEngine::CheckOutGet(IVSSItem *pIItem) { _ASSERTE(pIItem); HRESULT hr = S_OK; long iStatus = 0; // is files checked out? hr = pIItem->get_IsCheckedOut(&iStatus); if(FAILED(hr)) return hr; // check it out to me if(iStatus != VSSFILE_CHECKEDOUT_ME) hr = pIItem->Checkout(NULL,NULL,0); return hr; } HRESULT CVerEngine::CheckOutLocal(IVSSItem *pIItem,LPCTSTR szFileSpec) { _ASSERTE(pIItem); HRESULT hr = S_OK; long iStatus = 0; // is files checked out? hr = pIItem->get_IsCheckedOut(&iStatus); if(FAILED(hr)) return hr; // check it out to me if(iStatus != VSSFILE_CHECKEDOUT_ME) { hr = pIItem->Checkout(NULL,_bstr_t(szFileSpec),0); } else { CComBSTR bstrFileSpec(szFileSpec); hr = pIItem->Get(&bstrFileSpec,0); } return hr; } HRESULT CVerEngine::GetPrjEx(LPCTSTR szPrj,IVSSItem **hIPrj,bool bCreate) { _ASSERTE(hIPrj && szPrj); HRESULT hr = S_OK; *hIPrj = NULL; _bstr_t bstrPrj(szPrj); hr = m_pIDB->get_VSSItem(bstrPrj,false,hIPrj); if( hr == ESS_VS_NOT_FOUND && bCreate ) { // does it exist as delete hr = m_pIDB->get_VSSItem(bstrPrj,true,hIPrj); if(SUCCEEDED(hr)) { hr = (*hIPrj)->put_Deleted(false); // make sure it's not deleted } else if(hr == ESS_VS_NOT_FOUND) { // find the top-most prj that exists CComPtr pItmp; wstring sztmp = szPrj; int iPos = wstring::npos; while( hr == ESS_VS_NOT_FOUND ) { iPos = sztmp.find_last_of(L"/"); if(iPos == wstring::npos) return E_FAIL; sztmp = sztmp.substr(0,iPos).c_str(); if(sztmp.size() == 1) // if we reached $/ sztmp = L"$/"; // we need to have the / in $/ hr = m_pIDB->get_VSSItem(_bstr_t(sztmp.c_str()),false,&pItmp); } IF_FAIL_RTN1(hr,"get_VSSItem"); // add recursivly the remaining subprojects CComPtr pItmp2; int iPos2 = 0; sztmp = szPrj; _bstr_t bstrSubPrj; while( iPos2 != wstring::npos ) { ++iPos; iPos2 = sztmp.find_first_of(L"/",iPos); if(iPos2 == wstring::npos) bstrSubPrj = sztmp.substr(iPos,sztmp.length()-iPos).c_str(); else bstrSubPrj = sztmp.substr(iPos,iPos2-iPos).c_str(); hr = pItmp->NewSubproject(bstrSubPrj,NULL,&pItmp2); IF_FAIL_RTN1(hr,"NewSubproject"); iPos = iPos2; pItmp.Release(); pItmp = pItmp2; pItmp2.Release(); } *hIPrj = pItmp; (*hIPrj)->AddRef(); pItmp.Release(); hr = S_FALSE; // signal that we created it } } IF_FAIL_RTN1(hr,"get_VSSItem"); return hr; } HRESULT CVerEngine::GetItemEx(LPCTSTR szItem,IVSSItem **hIItem,bool bCreate) { _ASSERTE(hIItem && szItem); HRESULT hr = S_OK; *hIItem = NULL; _bstr_t bstrItem(szItem); hr = m_pIDB->get_VSSItem(bstrItem,false,hIItem); if( hr == ESS_VS_NOT_FOUND && bCreate ) { // does it exist as delete hr = m_pIDB->get_VSSItem(bstrItem,true,hIItem); if(SUCCEEDED(hr)) { hr = (*hIItem)->put_Deleted(false); // make sure it's not deleted IF_FAIL_RTN1(hr,"put_Deleted"); hr = S_FALSE; } else if(hr == ESS_VS_NOT_FOUND) { CComPtr pIPrj; // get prj wstring szItem = szItem; int iFileNameIndex = szItem.find_last_of(L"/"); if(iFileNameIndex == wstring::npos) return E_FAIL; hr = GetPrjEx(_bstr_t(szItem.substr(0,iFileNameIndex).c_str()),&pIPrj,bCreate); IF_FAIL_RTN1(hr,"GetPrjEx"); // add the file to the prj HANDLE hFile = NULL; TCHAR szTmpSpec[MAX_PATH]; BOOL b = FALSE; CComBSTR bstrFileSpec; // create an empty file szFileName in tmp dir GetTempPath(MAX_PATH,szTmpSpec); GetTempFileName(szTmpSpec,L"",0,szTmpSpec); // creates tmp file b = DeleteFile(szTmpSpec); // delete tmp file since we want tmp dir b = CreateDirectory(szTmpSpec,NULL); // create tmp dir bstrFileSpec = szTmpSpec; bstrFileSpec.Append(L"\\"); bstrFileSpec.Append(szItem.substr(iFileNameIndex+1).c_str()); hFile = CreateFile(bstrFileSpec, // create file in tmp dir GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL); CloseHandle(hFile); // add this file hr = pIPrj->Add(bstrFileSpec,NULL,VSSFLAG_KEEPYES,hIItem); b = DeleteFile(bstrFileSpec); b = RemoveDirectory(szTmpSpec); hr = S_FALSE; } } else if(hr == ESS_VS_NOT_FOUND) return hr; IF_FAIL_RTN1(hr,"get_VSSItem"); return hr; } void CVerEngine::EliminateCommon(list &ListOne, list &ListTwo) { int sizeOne = ListOne.size(); int sizeTwo = ListTwo.size(); if(sizeOne == 0 || sizeTwo == 0) return; list &List1 = ListTwo; list &List2 = ListOne; if(sizeOne >= sizeTwo) { List1 = ListOne; List2 = ListTwo; } list::iterator i; list::iterator j; for(i = List1.begin(); i != List1.end(); ++i) { for(j = List2.begin(); j != List2.end(); ++j) { if((*i).compare(*j) == 0) { List1.erase(i); List2.erase(j); break; } } } } HRESULT CVerEngine::SyncPrj(LPCTSTR szBasePrj,LPCTSTR szDir) { bool result = true; typedef list wstringlist; wstringlist FileList; wstringlist DirList; WIN32_FIND_DATA finddata; HANDLE hFind = FindFirstFile( wstring(szDir).append(L"\\*.*").c_str(), &finddata); if(hFind == INVALID_HANDLE_VALUE && GetLastError() != ERROR_NO_MORE_FILES) return GetLastError(); do { if(finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) DirList.insert(DirList.end(),finddata.cFileName); else FileList.insert(FileList.end(),finddata.cFileName); } while(FindNextFile(hFind,&finddata)); FindClose(hFind); hFind = 0; HRESULT hr; wstringlist::iterator i; for(i = FileList.begin(); i != FileList.end(); ++i) { hr = Sync(szBasePrj, szDir, (*i).c_str()); IF_FAIL_RTN1(hr,"Sync"); } for(i = DirList.begin(); i != DirList.end(); ++i) { } return S_OK; }