void SubversionPathTest() { CTSVNPath testPath; testPath.SetFromWin(L"c:\\"); ATLASSERT(strcmp(testPath.GetSVNApiPath(pool), "C:/") == 0); testPath.SetFromWin(L"c:\\folder"); ATLASSERT(strcmp(testPath.GetSVNApiPath(pool), "C:/folder") == 0); testPath.SetFromWin(L"c:\\a\\b\\c\\d\\e"); ATLASSERT(strcmp(testPath.GetSVNApiPath(pool), "C:/a/b/c/d/e") == 0); testPath.SetFromUnknown(L"http://testing/"); ATLASSERT(strcmp(testPath.GetSVNApiPath(pool), "http://testing") == 0); testPath.SetFromSVN(NULL); ATLASSERT(strlen(testPath.GetSVNApiPath(pool))==0); testPath.SetFromWin(L"\\\\a\\b\\c\\d\\e"); ATLASSERT(strcmp(testPath.GetSVNApiPath(pool), "//a/b/c/d/e") == 0); testPath.SetFromWin(L"\\\\?\\C:\\Windows"); ATLASSERT(wcscmp(testPath.GetWinPath(), L"C:\\Windows")==0); testPath.SetFromUnknown(L"\\\\?\\C:\\Windows"); ATLASSERT(wcscmp(testPath.GetWinPath(), L"C:\\Windows")==0); #if defined(_MFC_VER) testPath.SetFromUnknown(L"http://testing again"); ATLASSERT(strcmp(testPath.GetSVNApiPath(pool), "http://testing%20again") == 0); testPath.SetFromUnknown(L"http://testing%20again"); ATLASSERT(strcmp(testPath.GetSVNApiPath(pool), "http://testing%20again") == 0); testPath.SetFromUnknown(L"http://testing special chars \344\366\374"); ATLASSERT(strcmp(testPath.GetSVNApiPath(pool), "http://testing%20special%20chars%20%c3%a4%c3%b6%c3%bc") == 0); #endif }
VOID GetAnswerToRequest(const TSVNCacheRequest* pRequest, TSVNCacheResponse* pReply, DWORD* pResponseLength) { CTSVNPath path; *pResponseLength = 0; if(pRequest->flags & TSVNCACHE_FLAGS_FOLDERISKNOWN) { path.SetFromWin(pRequest->path, !!(pRequest->flags & TSVNCACHE_FLAGS_ISFOLDER)); } else { path.SetFromWin(pRequest->path); } CAutoReadWeakLock readLock(CSVNStatusCache::Instance().GetGuard(), 2000); if (readLock.IsAcquired()) { CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": app asked for status of %s\n", pRequest->path); CSVNStatusCache::Instance().GetStatusForPath(path, pRequest->flags, false).BuildCacheResponse(*pReply, *pResponseLength); } else { CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": timeout for asked status of %s\n", pRequest->path); CStatusCacheEntry entry; entry.BuildCacheResponse(*pReply, *pResponseLength); } }
BOOL ShellCache::IsVersioned(LPCTSTR path, bool bIsDir, bool mustbeok) { tstring folder (path); if (! bIsDir) { size_t pos = folder.rfind ('\\'); if (pos != tstring::npos) folder.erase (pos); } std::map<tstring, BoolTimeout>::iterator iter; if ((iter = admindircache.find(folder)) != admindircache.end()) { if ((GetTickCount64() - iter->second.timeout) < ADMINDIRTIMEOUT) return iter->second.bBool; } BoolTimeout bt; CTSVNPath p; p.SetFromWin(folder.c_str()); bt.bBool = SVNHelper::IsVersioned(p, mustbeok); bt.timeout = GetTickCount64(); Locker lock(m_critSec); admindircache[folder] = bt; return bt.bBool; }
void AncestorTest() { CTSVNPath testPath; testPath.SetFromWin(L"c:\\windows"); ATLASSERT(testPath.IsAncestorOf(CTSVNPath(L"c:\\"))==false); ATLASSERT(testPath.IsAncestorOf(CTSVNPath(L"c:\\windows"))); ATLASSERT(testPath.IsAncestorOf(CTSVNPath(L"c:\\windowsdummy"))==false); ATLASSERT(testPath.IsAncestorOf(CTSVNPath(L"c:\\windows\\test.txt"))); ATLASSERT(testPath.IsAncestorOf(CTSVNPath(L"c:\\windows\\system32\\test.txt"))); }
LRESULT CLockDlg::OnFileDropped(WPARAM, LPARAM lParam) { BringWindowToTop(); SetForegroundWindow(); SetActiveWindow(); // if multiple files/folders are dropped // this handler is called for every single item // separately. // To avoid creating multiple refresh threads and // causing crashes, we only add the items to the // list control and start a timer. // When the timer expires, we start the refresh thread, // but only if it isn't already running - otherwise we // restart the timer. CTSVNPath path; path.SetFromWin((LPCTSTR)lParam); if (!m_cFileList.HasPath(path)) { if (m_pathList.AreAllPathsFiles()) { m_pathList.AddPath(path); m_pathList.RemoveDuplicates(); } else { // if the path list contains folders, we have to check whether // our just (maybe) added path is a child of one of those. If it is // a child of a folder already in the list, we must not add it. Otherwise // that path could show up twice in the list. bool bHasParentInList = false; for (int i=0; i<m_pathList.GetCount(); ++i) { if (m_pathList[i].IsAncestorOf(path)) { bHasParentInList = true; break; } } if (!bHasParentInList) { m_pathList.AddPath(path); m_pathList.RemoveDuplicates(); } } } // Always start the timer, since the status of an existing item might have changed SetTimer(REFRESHTIMER, 200, NULL); CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Item %s dropped, timer started\n", path.GetWinPath()); return 0; }
bool CSVNStatusCache::RemoveCacheForDirectory(CCachedDirectory * cdir) { if (cdir == NULL) return false; CAutoWriteLock writeLock(m_guard); if (!cdir->m_childDirectories.empty()) { auto it = cdir->m_childDirectories.begin(); for (; it != cdir->m_childDirectories.end(); ) { CTSVNPath path; CString winPath = CUnicodeUtils::GetUnicode (it->first); path.SetFromWin (winPath, true); CCachedDirectory * childdir = CSVNStatusCache::Instance().GetDirectoryCacheEntryNoCreate(path); if ((childdir)&&(!cdir->m_directoryPath.IsEquivalentTo(childdir->m_directoryPath))&&(cdir->m_directoryPath.GetFileOrDirectoryName()!=L"..")) RemoveCacheForDirectory(childdir); cdir->m_childDirectories.erase(it->first); it = cdir->m_childDirectories.begin(); } } cdir->m_childDirectories.clear(); m_directoryCache.erase(cdir->m_directoryPath); // we could have entries versioned and/or stored in our cache which are // children of the specified directory, but not in the m_childDirectories // member: this can happen for nested layouts or if we fetched the status // while e.g., an update/checkout was in progress CCachedDirectory::ItDir itMap = m_directoryCache.lower_bound(cdir->m_directoryPath); do { if (itMap != m_directoryCache.end()) { if (cdir->m_directoryPath.IsAncestorOf(itMap->first)) { // just in case (see issue #255) if (itMap->second == cdir) { m_directoryCache.erase(itMap); } else RemoveCacheForDirectory(itMap->second); } } itMap = m_directoryCache.lower_bound(cdir->m_directoryPath); } while (itMap != m_directoryCache.end() && cdir->m_directoryPath.IsAncestorOf(itMap->first)); CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": removed from cache %s\n", cdir->m_directoryPath.GetWinPath()); delete cdir; return true; }
void ContainingDirectoryTest() { CTSVNPath testPath; testPath.SetFromWin(L"c:\\a\\b\\c\\d\\e"); CTSVNPath dir; dir = testPath.GetContainingDirectory(); ATLASSERT(dir.GetWinPathString() == L"c:\\a\\b\\c\\d"); dir = dir.GetContainingDirectory(); ATLASSERT(dir.GetWinPathString() == L"c:\\a\\b\\c"); dir = dir.GetContainingDirectory(); ATLASSERT(dir.GetWinPathString() == L"c:\\a\\b"); dir = dir.GetContainingDirectory(); ATLASSERT(dir.GetWinPathString() == L"c:\\a"); dir = dir.GetContainingDirectory(); ATLASSERT(dir.GetWinPathString() == L"c:\\"); dir = dir.GetContainingDirectory(); ATLASSERT(dir.IsEmpty()); ATLASSERT(dir.GetWinPathString().IsEmpty()); }
CTSVNPath CTSVNPath::GetContainingDirectory() const { EnsureBackslashPathSet(); CString sDirName = m_sBackslashPath.Left(m_sBackslashPath.ReverseFind('\\')); if(sDirName.GetLength() == 2 && sDirName[1] == ':') { // This is a root directory, which needs a trailing slash sDirName += '\\'; if(sDirName == m_sBackslashPath) { // We were clearly provided with a root path to start with - we should return nothing now sDirName.Empty(); } } if(sDirName.GetLength() == 1 && sDirName[0] == '\\') { // We have an UNC path and we already are the root sDirName.Empty(); } CTSVNPath retVal; retVal.SetFromWin(sDirName); return retVal; }
void ValidPathAndUrlTest() { CTSVNPath testPath; testPath.SetFromWin(L"c:\\a\\b\\c.test.txt"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"D:\\.Net\\SpindleSearch\\"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\test folder\\file"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\folder\\"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\ext.ext.ext\\ext.ext.ext.ext"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\.svn"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\com\\file"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\test\\conf"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\LPT"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\test\\LPT"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\com1test"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"\\\\?\\c:\\test\\com1test"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"\\\\Share\\filename"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"\\\\Share\\filename.extension"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromWin(L"\\\\Share\\.svn"); ATLASSERT(testPath.IsValidOnWindows()); // now the negative tests testPath.SetFromWin(L"c:\\test:folder"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\file<name"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\something*else"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\folder\\file?nofile"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\ext.>ension"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\com1\\filename"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\com1"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromWin(L"c:\\com1\\AuX"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromWin(L"\\\\Share\\lpt9\\filename"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromWin(L"\\\\Share\\prn"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromWin(L"\\\\Share\\NUL"); ATLASSERT(!testPath.IsValidOnWindows()); // now come some URL tests testPath.SetFromSVN(L"http://myserver.com/repos/trunk"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromSVN(L"https://myserver.com/repos/trunk/file%20with%20spaces"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromSVN(L"svn://myserver.com/repos/trunk/file with spaces"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromSVN(L"svn+ssh://www.myserver.com/repos/trunk"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromSVN(L"http://localhost:90/repos/trunk"); ATLASSERT(testPath.IsValidOnWindows()); testPath.SetFromSVN(L"file:///C:/SVNRepos/Tester/Proj1/tags/t2"); ATLASSERT(testPath.IsValidOnWindows()); // and some negative URL tests testPath.SetFromSVN(L"https://myserver.com/rep:os/trunk/file%20with%20spaces"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromSVN(L"svn://myserver.com/rep<os/trunk/file with spaces"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromSVN(L"svn+ssh://www.myserver.com/repos/trunk/prn/"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromSVN(L"http://localhost:90/repos/trunk/com1"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromSVN(L"http://localhost:90/repos/trunk/Blame3-%3Eblame.cpp"); ATLASSERT(!testPath.IsValidOnWindows()); testPath.SetFromSVN(L""); ATLASSERT(!testPath.IsUrl()); }
bool DropMoveCommand::Execute() { CString droppath = parser.GetVal(L"droptarget"); if (CTSVNPath(droppath).IsAdminDir()) return FALSE; SVN svn; unsigned long count = 0; pathList.RemoveAdminPaths(); CString sNewName; if ((parser.HasKey(L"rename"))&&(pathList.GetCount()==1)) { // ask for a new name of the source item CRenameDlg renDlg; renDlg.SetFileSystemAutoComplete(); renDlg.SetInputValidator(this); renDlg.m_windowtitle.LoadString(IDS_PROC_MOVERENAME); renDlg.m_name = pathList[0].GetFileOrDirectoryName(); if (renDlg.DoModal() != IDOK) { return FALSE; } sNewName = renDlg.m_name; } CProgressDlg progress; if (progress.IsValid()) { progress.SetTitle(IDS_PROC_MOVING); progress.SetTime(true); progress.ShowModeless(CWnd::FromHandle(GetExplorerHWND())); } UINT msgRet = IDNO; INT_PTR msgRetNonversioned = 0; for (int nPath = 0; nPath < pathList.GetCount(); nPath++) { CTSVNPath destPath; if (sNewName.IsEmpty()) destPath = CTSVNPath(droppath+L"\\"+pathList[nPath].GetFileOrDirectoryName()); else destPath = CTSVNPath(droppath+L"\\"+sNewName); // path the same but case-changed is ok: results in a case-rename if (!(pathList[nPath].IsEquivalentToWithoutCase(destPath) && !pathList[nPath].IsEquivalentTo(destPath))) { if (destPath.Exists()) { progress.Stop(); CString name = pathList[nPath].GetFileOrDirectoryName(); if (!sNewName.IsEmpty()) name = sNewName; progress.Stop(); CRenameDlg dlg; dlg.SetFileSystemAutoComplete(); dlg.SetInputValidator(this); dlg.m_name = name; dlg.m_windowtitle.Format(IDS_PROC_NEWNAMEMOVE, (LPCTSTR)name); if (dlg.DoModal() != IDOK) { return FALSE; } destPath.SetFromWin(droppath+L"\\"+dlg.m_name); progress.EnsureValid(); progress.SetTitle(IDS_PROC_MOVING); progress.SetTime(true); progress.SetProgress(count, pathList.GetCount()); progress.ShowModeless(CWnd::FromHandle(GetExplorerHWND())); } } if (!svn.Move(CTSVNPathList(pathList[nPath]), destPath)) { if ((svn.GetSVNError() && svn.GetSVNError()->apr_err == SVN_ERR_ENTRY_EXISTS) && (destPath.Exists())) { if ((msgRet != IDYESTOALL) && (msgRet != IDNOTOALL)) { progress.Stop(); // target file already exists. Ask user if he wants to replace the file CString sReplace; sReplace.Format(IDS_PROC_REPLACEEXISTING, destPath.GetWinPath()); CTaskDialog taskdlg(sReplace, CString(MAKEINTRESOURCE(IDS_PROC_REPLACEEXISTING_TASK2)), L"TortoiseSVN", 0, TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW | TDF_SIZE_TO_CONTENT); taskdlg.AddCommandControl(1, CString(MAKEINTRESOURCE(IDS_PROC_REPLACEEXISTING_TASK3))); taskdlg.AddCommandControl(2, CString(MAKEINTRESOURCE(IDS_PROC_REPLACEEXISTING_TASK4))); taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON); taskdlg.SetVerificationCheckboxText(CString(MAKEINTRESOURCE(IDS_PROC_REPLACEEXISTING_TASK5))); taskdlg.SetVerificationCheckbox(false); taskdlg.SetDefaultCommandControl(2); taskdlg.SetMainIcon(TD_WARNING_ICON); INT_PTR ret = taskdlg.DoModal(GetExplorerHWND()); if (ret == 1) // replace msgRet = taskdlg.GetVerificationCheckboxState() ? IDYESTOALL : IDYES; else msgRet = taskdlg.GetVerificationCheckboxState() ? IDNOTOALL : IDNO; progress.EnsureValid(); progress.SetTitle(IDS_PROC_MOVING); progress.SetTime(true); progress.SetProgress(count, pathList.GetCount()); progress.ShowModeless(CWnd::FromHandle(GetExplorerHWND())); } if ((msgRet == IDYES) || (msgRet == IDYESTOALL)) { if (!svn.Remove(CTSVNPathList(destPath), true, false)) { destPath.Delete(true); } if (!svn.Move(CTSVNPathList(pathList[nPath]), destPath)) { progress.Stop(); svn.ShowErrorDialog(GetExplorerHWND(), pathList[nPath]); return FALSE; //get out of here } CShellUpdater::Instance().AddPathForUpdate(destPath); } } else if (svn.GetSVNError() && svn.GetSVNError()->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) { INT_PTR ret = 0; if (msgRetNonversioned == 0) { progress.Stop(); CString sReplace; sReplace.Format(IDS_PROC_MOVEUNVERSIONED_TASK1, destPath.GetWinPath()); CTaskDialog taskdlg(sReplace, CString(MAKEINTRESOURCE(IDS_PROC_MOVEUNVERSIONED_TASK2)), L"TortoiseSVN", TDCBF_CANCEL_BUTTON, TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW | TDF_SIZE_TO_CONTENT); taskdlg.AddCommandControl(101, CString(MAKEINTRESOURCE(IDS_PROC_MOVEUNVERSIONED_TASK3))); taskdlg.AddCommandControl(102, CString(MAKEINTRESOURCE(IDS_PROC_MOVEUNVERSIONED_TASK4))); taskdlg.AddCommandControl(103, CString(MAKEINTRESOURCE(IDS_PROC_MOVEUNVERSIONED_TASK5))); taskdlg.SetVerificationCheckboxText(CString(MAKEINTRESOURCE(IDS_PROC_MOVEUNVERSIONED_TASK6))); taskdlg.SetVerificationCheckbox(false); taskdlg.SetDefaultCommandControl(103); taskdlg.SetMainIcon(TD_WARNING_ICON); ret = taskdlg.DoModal(GetExplorerHWND()); if (taskdlg.GetVerificationCheckboxState()) msgRetNonversioned = ret; progress.EnsureValid(); progress.SetTitle(IDS_PROC_MOVING); progress.SetTime(true); progress.SetProgress(count, pathList.GetCount()); progress.ShowModeless(CWnd::FromHandle(GetExplorerHWND())); } else { ret = msgRetNonversioned; } switch (ret) { case 101: // move MoveFile(pathList[nPath].GetWinPath(), destPath.GetWinPath()); break; case 102: // move and add MoveFile(pathList[nPath].GetWinPath(), destPath.GetWinPath()); if (!svn.Add(CTSVNPathList(destPath), NULL, svn_depth_infinity, true, false, false, false)) { progress.Stop(); svn.ShowErrorDialog(GetExplorerHWND(), destPath); return FALSE; //get out of here } break; case 103: // skip default: break; } } else { progress.Stop(); svn.ShowErrorDialog(GetExplorerHWND(), pathList[nPath]); return FALSE; //get out of here } } else CShellUpdater::Instance().AddPathForUpdate(destPath); count++; if (progress.IsValid()) { progress.FormatPathLine(1, IDS_PROC_MOVINGPROG, pathList[nPath].GetWinPath()); progress.FormatPathLine(2, IDS_PROC_CPYMVPROG2, destPath.GetWinPath()); progress.SetProgress(count, pathList.GetCount()); } if ((progress.IsValid())&&(progress.HasUserCancelled())) { progress.Stop(); TaskDialog(GetExplorerHWND(), AfxGetResourceHandle(), MAKEINTRESOURCE(IDS_APPNAME), MAKEINTRESOURCE(IDS_SVN_USERCANCELLED), NULL, TDCBF_OK_BUTTON, TD_INFORMATION_ICON, NULL); return FALSE; } } return true; }
unsigned int __stdcall CommandThread(LPVOID lpvParam) { CCrashReportThread crashthread; CTraceToOutputDebugString::Instance()(__FUNCTION__ ": CommandThread started\n"); DWORD cbBytesRead; CAutoFile hPipe; // The thread's parameter is a handle to a pipe instance. hPipe = std::move((HANDLE) lpvParam); while (bRun) { // Read client requests from the pipe. TSVNCacheCommand command; BOOL fSuccess = ReadFile( hPipe, // handle to pipe &command, // buffer to receive data sizeof(command), // size of buffer &cbBytesRead, // number of bytes read NULL); // not overlapped I/O if (! fSuccess || cbBytesRead == 0) { DisconnectNamedPipe(hPipe); CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Command thread exited\n"); return 1; } // sanitize request: // * Make sure the string properly 0-terminated // by resetting overlong paths to the empty string // * Set all trailing chars to 0. // This is more or less paranoia code but maybe something // is feeding garbage into our queue. for (size_t i = MAX_PATH+1; (i > 0) && (command.path[i-1] != 0); --i) command.path[i-1] = 0; size_t pathLength = wcslen (command.path); SecureZeroMemory ( command.path + pathLength , sizeof (command.path) - pathLength * sizeof (TCHAR)); // process request switch (command.command) { case TSVNCACHECOMMAND_END: FlushFileBuffers(hPipe); DisconnectNamedPipe(hPipe); CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Command thread exited\n"); return 0; case TSVNCACHECOMMAND_CRAWL: { CTSVNPath changedpath; changedpath.SetFromWin(command.path, true); // remove the path from our cache - that will 'invalidate' it. { CAutoWriteLock writeLock(CSVNStatusCache::Instance().GetGuard()); CSVNStatusCache::Instance().RemoveCacheForPath(changedpath); } CSVNStatusCache::Instance().AddFolderForCrawling(changedpath.GetDirectory()); } break; case TSVNCACHECOMMAND_REFRESHALL: { CAutoWriteLock writeLock(CSVNStatusCache::Instance().GetGuard()); CSVNStatusCache::Instance().Refresh(); } break; case TSVNCACHECOMMAND_RELEASE: { CTSVNPath changedpath; changedpath.SetFromWin(command.path, true); CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": release handle for path %s\n", changedpath.GetWinPath()); CAutoWriteLock writeLock(CSVNStatusCache::Instance().GetGuard()); CSVNStatusCache::Instance().CloseWatcherHandles(changedpath); CSVNStatusCache::Instance().RemoveCacheForPath(changedpath); } break; case TSVNCACHECOMMAND_BLOCK: { CTSVNPath changedpath; changedpath.SetFromWin(command.path); CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": block path %s\n", changedpath.GetWinPath()); CSVNStatusCache::Instance().BlockPath(changedpath, false); } break; case TSVNCACHECOMMAND_UNBLOCK: { CTSVNPath changedpath; changedpath.SetFromWin(command.path); CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": unblock path %s\n", changedpath.GetWinPath()); CSVNStatusCache::Instance().UnBlockPath(changedpath); } break; } } // Flush the pipe to allow the client to read the pipe's contents // before disconnecting. Then disconnect the pipe, and close the // handle to this pipe instance. FlushFileBuffers(hPipe); DisconnectNamedPipe(hPipe); CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Command thread exited\n"); return 0; }
bool CheckoutCommand::Execute() { bool bRet = false; // Get the directory supplied in the command line. If there isn't // one then we should use first the default checkout path // specified in the settings dialog, and fall back to the current // working directory instead if no such path was specified. CTSVNPath checkoutDirectory; CRegString regDefCheckoutPath(_T("Software\\TortoiseSVN\\DefaultCheckoutPath")); if (cmdLinePath.IsEmpty()) { if (CString(regDefCheckoutPath).IsEmpty()) { checkoutDirectory.SetFromWin(sOrigCWD, true); DWORD len = ::GetTempPath(0, NULL); std::unique_ptr<TCHAR[]> tszPath(new TCHAR[len]); ::GetTempPath(len, tszPath.get()); if (_tcsncicmp(checkoutDirectory.GetWinPath(), tszPath.get(), len-2 /* \\ and \0 */) == 0) { // if the current directory is set to a temp directory, // we don't use that but leave it empty instead. checkoutDirectory.Reset(); } } else { checkoutDirectory.SetFromWin(CString(regDefCheckoutPath)); } } else { checkoutDirectory = cmdLinePath; } CCheckoutDlg dlg; dlg.m_URLs.LoadFromAsteriskSeparatedString (parser.GetVal(_T("url"))); if (dlg.m_URLs.GetCount()==0) { SVN svn; if (svn.IsRepository(cmdLinePath)) { CString url; // The path points to a local repository. // Add 'file:///' so the repository browser recognizes // it as an URL to the local repository. if (cmdLinePath.GetWinPathString().GetAt(0) == '\\') // starts with '\' means an UNC path { CString p = cmdLinePath.GetWinPathString(); p.TrimLeft('\\'); url = _T("file://")+p; } else url = _T("file:///")+cmdLinePath.GetWinPathString(); url.Replace('\\', '/'); dlg.m_URLs.AddPath(CTSVNPath(url)); checkoutDirectory.AppendRawString(L"wc"); } } dlg.m_strCheckoutDirectory = checkoutDirectory.GetWinPathString(); // if there is no url specified on the command line, check if there's one // specified in the settings dialog to use as the default and use that CRegString regDefCheckoutUrl(_T("Software\\TortoiseSVN\\DefaultCheckoutUrl")); if (!CString(regDefCheckoutUrl).IsEmpty()) { // if the URL specified is a child of the default URL, we also // adjust the default checkout path // e.g. // Url specified on command line: http://server.com/repos/project/trunk/folder // Url specified as default : http://server.com/repos/project/trunk // checkout path specified : c:\work\project // --> // checkout path adjusted : c:\work\project\folder CTSVNPath clurl = dlg.m_URLs.GetCommonDirectory(); CTSVNPath defurl = CTSVNPath(CString(regDefCheckoutUrl)); if (defurl.IsAncestorOf(clurl)) { // the default url is the parent of the specified url if (CTSVNPath::CheckChild(CTSVNPath(CString(regDefCheckoutPath)), CTSVNPath(dlg.m_strCheckoutDirectory))) { dlg.m_strCheckoutDirectory = CString(regDefCheckoutPath) + clurl.GetWinPathString().Mid(defurl.GetWinPathString().GetLength()); dlg.m_strCheckoutDirectory.Replace(_T("\\\\"), _T("\\")); } } if (dlg.m_URLs.GetCount() == 0) dlg.m_URLs.AddPath (defurl); } for (int i = 0; i < dlg.m_URLs.GetCount(); ++i) { CString pathString = dlg.m_URLs[i].GetWinPathString(); if (pathString.Left(5).Compare(_T("tsvn:"))==0) { pathString = pathString.Mid(5); if (pathString.Find('?') >= 0) { dlg.Revision = SVNRev(pathString.Mid(pathString.Find('?')+1)); pathString = pathString.Left(pathString.Find('?')); } } dlg.m_URLs[i].SetFromWin (pathString); } if (parser.HasKey(_T("revision"))) { SVNRev Rev = SVNRev(parser.GetVal(_T("revision"))); dlg.Revision = Rev; } dlg.m_blockPathAdjustments = parser.HasKey(L"blockpathadjustments"); if (dlg.DoModal() == IDOK) { checkoutDirectory.SetFromWin(dlg.m_strCheckoutDirectory, true); CSVNProgressDlg progDlg; theApp.m_pMainWnd = &progDlg; bool useStandardCheckout = dlg.m_standardCheckout || ((dlg.m_URLs.GetCount() > 1) && dlg.m_bIndependentWCs); progDlg.SetCommand (useStandardCheckout ? dlg.m_checkoutDepths.size() ? CSVNProgressDlg::SVNProgress_SparseCheckout : CSVNProgressDlg::SVNProgress_Checkout : dlg.m_parentExists && (dlg.m_URLs.GetCount() == 1) ? CSVNProgressDlg::SVNProgress_Update : CSVNProgressDlg::SVNProgress_SingleFileCheckout); if (dlg.m_checkoutDepths.size()) progDlg.SetPathDepths(dlg.m_checkoutDepths); progDlg.SetAutoClose (parser); progDlg.SetOptions(dlg.m_bNoExternals ? ProgOptIgnoreExternals : ProgOptNone); progDlg.SetPathList(CTSVNPathList(checkoutDirectory)); progDlg.SetUrl(dlg.m_URLs.CreateAsteriskSeparatedString()); progDlg.SetRevision(dlg.Revision); progDlg.SetDepth(dlg.m_depth); progDlg.DoModal(); bRet = !progDlg.DidErrorsOccur(); } return bRet; }
void CCachedDirectory::RefreshStatus(bool bRecursive) { // Make sure that our own status is up-to-date GetStatusForMember(m_directoryPath,bRecursive); CTSVNPathList updatePathList; CTSVNPathList crawlPathList; CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": RefreshStatus for %s\n"), m_directoryPath.GetWinPath()); DWORD now = GetTickCount(); { // get the file write times with FindFirstFile/FindNextFile since those // APIs only access the folder, not each file individually. // This reduces the disk access a *lot*. std::map<CStringA, ULONGLONG> filetimes; WIN32_FIND_DATA FindFileData; CAutoFindFile hFind = FindFirstFile(m_directoryPath.GetWinPathString() + L"\\*.*", &FindFileData); if (hFind) { while (FindNextFile(hFind, &FindFileData)) { if ( (wcscmp(FindFileData.cFileName, L"..")==0) || (wcscmp(FindFileData.cFileName, L".")==0) ) continue; ULARGE_INTEGER ft; ft.LowPart = FindFileData.ftLastWriteTime.dwLowDateTime; ft.HighPart = FindFileData.ftLastWriteTime.dwHighDateTime; CStringA nameUTF8 = CUnicodeUtils::GetUTF8(FindFileData.cFileName); filetimes[nameUTF8] = ft.QuadPart; } hFind.CloseHandle(); // explicit close handle to shorten its life time } AutoLocker lock(m_critSec); // We also need to check if all our file members have the right date on them for (CacheEntryMap::iterator itMembers = m_entryCache.begin(); itMembers != m_entryCache.end(); ++itMembers) { if ((itMembers->first)&&(!itMembers->first.IsEmpty())) { CTSVNPath filePath (GetFullPathString (itMembers->first)); if (!filePath.IsEquivalentToWithoutCase(m_directoryPath)) { // we only have file members in our entry cache ATLASSERT(!itMembers->second.IsDirectory()); auto ftIt = filetimes.find(itMembers->first.Mid(1)); if (ftIt != filetimes.end()) { ULONGLONG ft = ftIt->second; if ((itMembers->second.HasExpired(now))||(!itMembers->second.DoesFileTimeMatch(ft))) { // We need to request this item as well updatePathList.AddPath(filePath); } } } } } if (bRecursive) { // crawl all sub folders too! Otherwise a change deep inside the // tree which has changed won't get propagated up the tree. for(ChildDirStatus::const_iterator it = m_childDirectories.begin(); it != m_childDirectories.end(); ++it) { CTSVNPath path; CString winPath = CUnicodeUtils::GetUnicode (it->first); path.SetFromWin (winPath, true); crawlPathList.AddPath(path); } } } for (int i = 0; i < updatePathList.GetCount(); ++i) GetStatusForMember(updatePathList[i], bRecursive); for (int i = 0; i < crawlPathList.GetCount(); ++i) CSVNStatusCache::Instance().AddFolderForCrawling(crawlPathList[i]); }
CStatusCacheEntry CCachedDirectory::GetStatusForMember(const CTSVNPath& path, bool bRecursive, bool bFetch /* = true */) { CStringA strCacheKey; bool bThisDirectoryIsUnversioned = false; bool bRequestForSelf = false; if(path.IsEquivalentToWithoutCase(m_directoryPath)) { bRequestForSelf = true; } // In all most circumstances, we ask for the status of a member of this directory. ATLASSERT(m_directoryPath.IsEquivalentToWithoutCase(path.GetContainingDirectory()) || bRequestForSelf); long long dbFileTime = CSVNStatusCache::Instance().WCRoots()->GetDBFileTime(m_directoryPath); bool wcDbFileTimeChanged = (m_wcDbFileTime != dbFileTime); if ( !wcDbFileTimeChanged ) { if(m_wcDbFileTime == 0) { // We are a folder which is not in a working copy bThisDirectoryIsUnversioned = true; m_ownStatus.SetStatus(NULL, false, false); // If a user removes the .svn directory, we get here with m_entryCache // not being empty, but still us being unversioned if (!m_entryCache.empty()) { m_entryCache.clear(); } ATLASSERT(m_entryCache.empty()); // However, a member *DIRECTORY* might be the top of WC // so we need to ask them to get their own status if(!path.IsDirectory()) { if ((PathFileExists(path.GetWinPath()))||(bRequestForSelf)) return CStatusCacheEntry(); // the entry doesn't exist anymore! // but we can't remove it from the cache here: // the GetStatusForMember() method is called only with a read // lock and not a write lock! // So mark it for crawling, and let the crawler remove it // later CSVNStatusCache::Instance().AddFolderForCrawling(path.GetContainingDirectory()); return CStatusCacheEntry(); } else { // If we're in the special case of a directory being asked for its own status // and this directory is unversioned, then we should just return that here if(bRequestForSelf) { return CStatusCacheEntry(); } } } if (CSVNStatusCache::Instance().GetDirectoryCacheEntryNoCreate(path) != NULL) { // We don't have directory status in our cache // Ask the directory if it knows its own status CCachedDirectory * dirEntry = CSVNStatusCache::Instance().GetDirectoryCacheEntry(path); if ((dirEntry)&&(dirEntry->IsOwnStatusValid())) { // To keep recursive status up to date, we'll request that children are all crawled again // We have to do this because the directory watcher isn't very reliable (especially under heavy load) // and also has problems with SUBSTed drives. // If nothing has changed in those directories, this crawling is fast and only // accesses two files for each directory. if (bRecursive) { AutoLocker lock(dirEntry->m_critSec); ChildDirStatus::const_iterator it; for(it = dirEntry->m_childDirectories.begin(); it != dirEntry->m_childDirectories.end(); ++it) { CTSVNPath newpath; CString winPath = CUnicodeUtils::GetUnicode (it->first); newpath.SetFromWin(winPath, true); CSVNStatusCache::Instance().AddFolderForCrawling(newpath); } } return dirEntry->GetOwnStatus(bRecursive); } } else { { // if we currently are fetching the status of the directory // we want the status for, we just return an empty entry here // and don't wait for that fetching to finish. // That's because fetching the status can take a *really* long // time (e.g. if a commit is also in progress on that same // directory), and we don't want to make the explorer appear // to hang. if ((!bFetch)&&(m_FetchingStatus)) { if (m_directoryPath.IsAncestorOf(path)) { m_currentFullStatus = m_mostImportantFileStatus = svn_wc_status_none; return GetCacheStatusForMember(path); } } } // Look up a file in our own cache AutoLocker lock(m_critSec); strCacheKey = GetCacheKey(path); CacheEntryMap::iterator itMap = m_entryCache.find(strCacheKey); if(itMap != m_entryCache.end()) { // We've hit the cache - check for timeout if(!itMap->second.HasExpired((long)GetTickCount())) { if(itMap->second.DoesFileTimeMatch(path.GetLastWriteTime())) { if ((itMap->second.GetEffectiveStatus()!=svn_wc_status_missing)||(!PathFileExists(path.GetWinPath()))) { // Note: the filetime matches after a modified has been committed too. // So in that case, we would return a wrong status (e.g. 'modified' instead // of 'normal') here. return itMap->second; } } } } } } else { if ((!bFetch)&&(m_FetchingStatus)) { if (m_directoryPath.IsAncestorOf(path)) { // returning empty status (status fetch in progress) // also set the status to 'none' to have the status change and // the shell updater invoked in the crawler m_currentFullStatus = m_mostImportantFileStatus = svn_wc_status_none; CSVNStatusCache::Instance().AddFolderForCrawling(m_directoryPath.GetDirectory()); return GetCacheStatusForMember(path); } } // if we're fetching the status for the explorer, // we don't refresh the status but use the one // we already have (to save time and make the explorer // more responsive in stress conditions). // We leave the refreshing to the crawler. if ((!bFetch)&&(m_wcDbFileTime)) { CSVNStatusCache::Instance().AddFolderForCrawling(m_directoryPath.GetDirectory()); return GetCacheStatusForMember(path); } AutoLocker lock(m_critSec); m_entryCache.clear(); strCacheKey = GetCacheKey(path); } // We've not got this item in the cache - let's add it // We never bother asking SVN for the status of just one file, always for its containing directory if (g_SVNAdminDir.IsAdminDirPath(path.GetWinPathString())) { // We're being asked for the status of an .SVN directory // It's not worth asking for this return CStatusCacheEntry(); } { if ((!bFetch)&&(m_FetchingStatus)) { if (m_directoryPath.IsAncestorOf(path)) { m_currentFullStatus = m_mostImportantFileStatus = svn_wc_status_none; return GetCacheStatusForMember(path); } } } { AutoLocker lock(m_critSec); m_mostImportantFileStatus = svn_wc_status_none; m_childDirectories.clear(); m_entryCache.clear(); m_ownStatus.SetStatus(NULL, false, false); } if(!bThisDirectoryIsUnversioned) { if (!SvnUpdateMembersStatus()) { m_wcDbFileTime = 0; return CStatusCacheEntry(); } } // Now that we've refreshed our SVN status, we can see if it's // changed the 'most important' status value for this directory. // If it has, then we should tell our parent UpdateCurrentStatus(); m_wcDbFileTime = dbFileTime; if (path.IsDirectory()) { CCachedDirectory * dirEntry = CSVNStatusCache::Instance().GetDirectoryCacheEntry(path); if ((dirEntry)&&(dirEntry->IsOwnStatusValid())) { //CSVNStatusCache::Instance().AddFolderForCrawling(path); return dirEntry->GetOwnStatus(bRecursive); } // If the status *still* isn't valid here, it means that // the current directory is unversioned, and we shall need to ask its children for info about themselves if ((dirEntry)&&(dirEntry != this)) return dirEntry->GetStatusForMember(path,bRecursive); // add the path for crawling: if it's really unversioned, the crawler will // only check for the admin dir and do nothing more. But if it is // versioned (could happen in a nested layout) the crawler will update its // status correctly CSVNStatusCache::Instance().AddFolderForCrawling(path); return CStatusCacheEntry(); } else { CacheEntryMap::iterator itMap = m_entryCache.find(strCacheKey); if(itMap != m_entryCache.end()) { return itMap->second; } } AddEntry(path, NULL, false, false); return CStatusCacheEntry(); }
bool CheckoutCommand::Execute() { bool bRet = false; // Get the directory supplied in the command line. If there isn't // one then we should use first the default checkout path // specified in the settings dialog, and fall back to the current // working directory instead if no such path was specified. CTSVNPath checkoutDirectory; CRegString regDefCheckoutPath(_T("Software\\TortoiseGit\\DefaultCheckoutPath")); if (cmdLinePath.IsEmpty()) { if (CString(regDefCheckoutPath).IsEmpty()) { checkoutDirectory.SetFromWin(sOrigCWD, true); DWORD len = ::GetTempPath(0, NULL); TCHAR * tszPath = new TCHAR[len]; ::GetTempPath(len, tszPath); if (_tcsncicmp(checkoutDirectory.GetWinPath(), tszPath, len-2 /* \\ and \0 */) == 0) { // if the current directory is set to a temp directory, // we don't use that but leave it empty instead. checkoutDirectory.Reset(); } delete [] tszPath; } else { checkoutDirectory.SetFromWin(CString(regDefCheckoutPath)); } } else { checkoutDirectory = cmdLinePath; } CCheckoutDlg dlg; dlg.m_strCheckoutDirectory = checkoutDirectory.GetWinPathString(); dlg.m_URL = parser.GetVal(_T("url")); // if there is no url specified on the command line, check if there's one // specified in the settings dialog to use as the default and use that CRegString regDefCheckoutUrl(_T("Software\\TortoiseGit\\DefaultCheckoutUrl")); if (!CString(regDefCheckoutUrl).IsEmpty()) { // if the URL specified is a child of the default URL, we also // adjust the default checkout path // e.g. // Url specified on command line: http://server.com/repos/project/trunk/folder // Url specified as default : http://server.com/repos/project/trunk // checkout path specified : c:\work\project // --> // checkout path adjusted : c:\work\project\folder CTSVNPath clurl = CTSVNPath(dlg.m_URL); CTSVNPath defurl = CTSVNPath(CString(regDefCheckoutUrl)); if (defurl.IsAncestorOf(clurl)) { // the default url is the parent of the specified url if (CTSVNPath::CheckChild(CTSVNPath(CString(regDefCheckoutPath)), CTSVNPath(dlg.m_strCheckoutDirectory))) { dlg.m_strCheckoutDirectory = CString(regDefCheckoutPath) + clurl.GetWinPathString().Mid(defurl.GetWinPathString().GetLength()); dlg.m_strCheckoutDirectory.Replace(_T("\\\\"), _T("\\")); } } if (dlg.m_URL.IsEmpty()) dlg.m_URL = regDefCheckoutUrl; } if (dlg.m_URL.Left(5).Compare(_T("tsvn:"))==0) { dlg.m_URL = dlg.m_URL.Mid(5); if (dlg.m_URL.Find('?') >= 0) { dlg.Revision = SVNRev(dlg.m_URL.Mid(dlg.m_URL.Find('?')+1)); dlg.m_URL = dlg.m_URL.Left(dlg.m_URL.Find('?')); } } if (parser.HasKey(_T("revision"))) { SVNRev Rev = SVNRev(parser.GetVal(_T("revision"))); dlg.Revision = Rev; } if (dlg.m_URL.Find('*')>=0) { // multiple URL's specified // ask where to check them out to CBrowseFolder foldbrowse; foldbrowse.SetInfo(CString(MAKEINTRESOURCE(IDS_PROC_CHECKOUTTO))); foldbrowse.SetCheckBoxText(CString(MAKEINTRESOURCE(IDS_PROC_CHECKOUTTOPONLY))); foldbrowse.SetCheckBoxText2(CString(MAKEINTRESOURCE(IDS_PROC_CHECKOUTNOEXTERNALS))); foldbrowse.m_style = BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS | BIF_USENEWUI | BIF_VALIDATE; TCHAR checkoutpath[MAX_PATH]; if (foldbrowse.Show(hwndExplorer, checkoutpath, MAX_PATH, CString(regDefCheckoutPath))==CBrowseFolder::OK) { CSVNProgressDlg progDlg; theApp.m_pMainWnd = &progDlg; if (parser.HasVal(_T("closeonend"))) progDlg.SetAutoClose(parser.GetLongVal(_T("closeonend"))); progDlg.SetCommand(CSVNProgressDlg::SVNProgress_Checkout); progDlg.SetOptions(foldbrowse.m_bCheck2 ? ProgOptIgnoreExternals : ProgOptNone); progDlg.SetPathList(CTSVNPathList(CTSVNPath(CString(checkoutpath)))); progDlg.SetUrl(dlg.m_URL); progDlg.SetRevision(dlg.Revision); progDlg.SetDepth(foldbrowse.m_bCheck ? svn_depth_empty : svn_depth_infinity); progDlg.DoModal(); bRet = !progDlg.DidErrorsOccur(); } } else if (dlg.DoModal() == IDOK) { checkoutDirectory.SetFromWin(dlg.m_strCheckoutDirectory, true); CSVNProgressDlg progDlg; theApp.m_pMainWnd = &progDlg; progDlg.SetCommand(CSVNProgressDlg::SVNProgress_Checkout); if (parser.HasVal(_T("closeonend"))) progDlg.SetAutoClose(parser.GetLongVal(_T("closeonend"))); progDlg.SetOptions(dlg.m_bNoExternals ? ProgOptIgnoreExternals : ProgOptNone); progDlg.SetPathList(CTSVNPathList(checkoutDirectory)); progDlg.SetUrl(dlg.m_URL); progDlg.SetRevision(dlg.Revision); progDlg.SetDepth(dlg.m_depth); progDlg.DoModal(); bRet = !progDlg.DidErrorsOccur(); } return bRet; }
bool PasteMoveCommand::Execute() { CString sDroppath = parser.GetVal(L"droptarget"); CTSVNPath dropPath(sDroppath); ProjectProperties props; props.ReadProps(dropPath); if (dropPath.IsAdminDir()) return FALSE; SVN svn; SVNStatus status; unsigned long count = 0; pathList.RemoveAdminPaths(); CString sNewName; CProgressDlg progress; progress.SetTitle(IDS_PROC_MOVING); progress.SetTime(true); progress.ShowModeless(CWnd::FromHandle(GetExplorerHWND())); for(int nPath = 0; nPath < pathList.GetCount(); nPath++) { CTSVNPath destPath; if (sNewName.IsEmpty()) destPath = CTSVNPath(sDroppath+L"\\"+pathList[nPath].GetFileOrDirectoryName()); else destPath = CTSVNPath(sDroppath+L"\\"+sNewName); if (destPath.Exists()) { CString name = pathList[nPath].GetFileOrDirectoryName(); if (!sNewName.IsEmpty()) name = sNewName; progress.Stop(); CRenameDlg dlg; dlg.SetFileSystemAutoComplete(); dlg.m_name = name; dlg.SetInputValidator(this); m_renPath = pathList[nPath]; dlg.m_windowtitle.Format(IDS_PROC_NEWNAMEMOVE, (LPCTSTR)name); if (dlg.DoModal() != IDOK) { return FALSE; } destPath.SetFromWin(sDroppath+L"\\"+dlg.m_name); } svn_wc_status_kind s = status.GetAllStatus(pathList[nPath]); if ((s == svn_wc_status_none)||(s == svn_wc_status_unversioned)||(s == svn_wc_status_ignored)) { // source file is unversioned: move the file to the target, then add it MoveFile(pathList[nPath].GetWinPath(), destPath.GetWinPath()); if (!svn.Add(CTSVNPathList(destPath), &props, svn_depth_infinity, true, true, false, true)) { svn.ShowErrorDialog(GetExplorerHWND()); return FALSE; //get out of here } CShellUpdater::Instance().AddPathForUpdate(destPath); } else { if (!svn.Move(CTSVNPathList(pathList[nPath]), destPath)) { svn.ShowErrorDialog(GetExplorerHWND()); return FALSE; //get out of here } else CShellUpdater::Instance().AddPathForUpdate(destPath); } count++; if (progress.IsValid()) { progress.FormatPathLine(1, IDS_PROC_MOVINGPROG, pathList[nPath].GetWinPath()); progress.FormatPathLine(2, IDS_PROC_CPYMVPROG2, destPath.GetWinPath()); progress.SetProgress(count, pathList.GetCount()); } if ((progress.IsValid())&&(progress.HasUserCancelled())) { TaskDialog(GetExplorerHWND(), AfxGetResourceHandle(), MAKEINTRESOURCE(IDS_APPNAME), MAKEINTRESOURCE(IDS_SVN_USERCANCELLED), NULL, TDCBF_OK_BUTTON, TD_INFORMATION_ICON, NULL); return FALSE; } } return true; }
bool DropMoveCommand::Execute() { CString droppath = parser.GetVal(_T("droptarget")); if (CTSVNPath(droppath).IsAdminDir()) return FALSE; SVN svn; unsigned long count = 0; pathList.RemoveAdminPaths(); CString sNewName; if ((parser.HasKey(_T("rename")))&&(pathList.GetCount()==1)) { // ask for a new name of the source item CRenameDlg renDlg; renDlg.SetInputValidator(this); renDlg.m_windowtitle.LoadString(IDS_PROC_MOVERENAME); renDlg.m_name = pathList[0].GetFileOrDirectoryName(); if (renDlg.DoModal() != IDOK) { return FALSE; } sNewName = renDlg.m_name; } CProgressDlg progress; if (progress.IsValid()) { progress.SetTitle(IDS_PROC_MOVING); progress.SetAnimation(IDR_MOVEANI); progress.SetTime(true); progress.ShowModeless(CWnd::FromHandle(GetExplorerHWND())); } UINT msgRet = IDNO; for(int nPath = 0; nPath < pathList.GetCount(); nPath++) { CTSVNPath destPath; if (sNewName.IsEmpty()) destPath = CTSVNPath(droppath+_T("\\")+pathList[nPath].GetFileOrDirectoryName()); else destPath = CTSVNPath(droppath+_T("\\")+sNewName); // path the same but case-changed is ok: results in a case-rename if (!(pathList[nPath].IsEquivalentToWithoutCase(destPath) && !pathList[nPath].IsEquivalentTo(destPath))) { if (destPath.Exists()) { CString name = pathList[nPath].GetFileOrDirectoryName(); if (!sNewName.IsEmpty()) name = sNewName; progress.Stop(); CRenameDlg dlg; dlg.SetInputValidator(this); dlg.m_name = name; dlg.m_windowtitle.Format(IDS_PROC_NEWNAMEMOVE, (LPCTSTR)name); if (dlg.DoModal() != IDOK) { return FALSE; } destPath.SetFromWin(droppath+_T("\\")+dlg.m_name); } } if (!svn.Move(CTSVNPathList(pathList[nPath]), destPath)) { if ((svn.GetSVNError() && svn.GetSVNError()->apr_err == SVN_ERR_ENTRY_EXISTS) && (destPath.Exists())) { if ((msgRet != IDYESTOALL) && (msgRet != IDNOTOALL)) { // target file already exists. Ask user if he wants to replace the file CString sReplace; sReplace.Format(IDS_PROC_REPLACEEXISTING, destPath.GetWinPath()); if (CTaskDialog::IsSupported()) { CTaskDialog taskdlg(sReplace, CString(MAKEINTRESOURCE(IDS_PROC_REPLACEEXISTING_TASK2)), L"TortoiseSVN", 0, TDF_USE_COMMAND_LINKS|TDF_ALLOW_DIALOG_CANCELLATION|TDF_POSITION_RELATIVE_TO_WINDOW); taskdlg.AddCommandControl(1, CString(MAKEINTRESOURCE(IDS_PROC_REPLACEEXISTING_TASK3))); taskdlg.AddCommandControl(2, CString(MAKEINTRESOURCE(IDS_PROC_REPLACEEXISTING_TASK4))); taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON); taskdlg.SetVerificationCheckboxText(CString(MAKEINTRESOURCE(IDS_PROC_REPLACEEXISTING_TASK5))); taskdlg.SetVerificationCheckbox(false); taskdlg.SetDefaultCommandControl(2); taskdlg.SetMainIcon(TD_WARNING_ICON); INT_PTR ret = taskdlg.DoModal(GetExplorerHWND()); if (ret == 1) // replace msgRet = taskdlg.GetVerificationCheckboxState() ? IDYES : IDYESTOALL; else msgRet = taskdlg.GetVerificationCheckboxState() ? IDNO : IDNOTOALL; } else { msgRet = TSVNMessageBox(GetExplorerHWND(), sReplace, _T("TortoiseSVN"), MB_ICONQUESTION|MB_YESNO|MB_YESTOALL|MB_NOTOALL); } } if ((msgRet == IDYES) || (msgRet == IDYESTOALL)) { if (!svn.Remove(CTSVNPathList(destPath), true, false)) { destPath.Delete(true); } if (!svn.Move(CTSVNPathList(pathList[nPath]), destPath)) { svn.ShowErrorDialog(GetExplorerHWND(), pathList[nPath]); return FALSE; //get out of here } CShellUpdater::Instance().AddPathForUpdate(destPath); } } else { svn.ShowErrorDialog(GetExplorerHWND(), pathList[nPath]); return FALSE; //get out of here } } else CShellUpdater::Instance().AddPathForUpdate(destPath); count++; if (progress.IsValid()) { progress.FormatPathLine(1, IDS_PROC_MOVINGPROG, pathList[nPath].GetWinPath()); progress.FormatPathLine(2, IDS_PROC_CPYMVPROG2, destPath.GetWinPath()); progress.SetProgress(count, pathList.GetCount()); } if ((progress.IsValid())&&(progress.HasUserCancelled())) { TSVNMessageBox(GetExplorerHWND(), IDS_SVN_USERCANCELLED, IDS_APPNAME, MB_ICONINFORMATION); return FALSE; } } return true; }
DWORD CHooks::RunScript(CString cmd, const CTSVNPathList& paths, CString& error, bool bWait, bool bShow) { DWORD exitcode = 0; SECURITY_ATTRIBUTES sa; SecureZeroMemory(&sa, sizeof(sa)); sa.nLength = sizeof(sa); sa.bInheritHandle = TRUE; CTSVNPath curDir = paths.GetCommonRoot().GetDirectory(); while (!curDir.IsEmpty() && !curDir.Exists()) curDir = curDir.GetContainingDirectory(); if (curDir.IsEmpty()) { WCHAR buf[MAX_PATH] = {0}; GetTempPath(MAX_PATH, buf); curDir.SetFromWin(buf, true); } CAutoFile hOut ; CAutoFile hRedir; CAutoFile hErr; // clear the error string error.Empty(); // Create Temp File for redirection TCHAR szTempPath[MAX_PATH] = { 0 }; TCHAR szOutput[MAX_PATH] = { 0 }; TCHAR szErr[MAX_PATH] = { 0 }; GetTempPath(_countof(szTempPath),szTempPath); GetTempFileName(szTempPath, L"svn", 0, szErr); // setup redirection handles // output handle must be WRITE mode, share READ // redirect handle must be READ mode, share WRITE hErr = CreateFile(szErr, GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, 0); if (!hErr) { error = CFormatMessageWrapper(); return (DWORD)-1; } hRedir = CreateFile(szErr, GENERIC_READ, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); if (!hRedir) { error = CFormatMessageWrapper(); return (DWORD)-1; } GetTempFileName(szTempPath, L"svn", 0, szOutput); hOut = CreateFile(szOutput, GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, 0); if (!hOut) { error = CFormatMessageWrapper(); return (DWORD)-1; } // setup startup info, set std out/err handles // hide window STARTUPINFO si; SecureZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.hStdOutput = hOut; si.hStdError = hErr; si.wShowWindow = bShow ? SW_SHOW : SW_HIDE; PROCESS_INFORMATION pi; SecureZeroMemory(&pi, sizeof(pi)); if (!CreateProcess(NULL, cmd.GetBuffer(), NULL, NULL, TRUE, 0, NULL, curDir.IsEmpty() ? NULL : curDir.GetWinPath(), &si, &pi)) { const DWORD err = GetLastError(); // preserve the CreateProcess error error = CFormatMessageWrapper(err); SetLastError(err); cmd.ReleaseBuffer(); return (DWORD)-1; } cmd.ReleaseBuffer(); CloseHandle(pi.hThread); // wait for process to finish, capture redirection and // send it to the parent window/console if (bWait) { DWORD dw; char buf[10*1024]; do { SecureZeroMemory(&buf,sizeof(buf)); while (ReadFile(hRedir, &buf, sizeof(buf)-1, &dw, NULL)) { if (dw == 0) break; error += CString(CStringA(buf,dw)); SecureZeroMemory(&buf,sizeof(buf)); } } while (WaitForSingleObject(pi.hProcess, 100) != WAIT_OBJECT_0); // perform any final flushing while (ReadFile(hRedir, &buf, sizeof(buf)-1, &dw, NULL)) { if (dw == 0) break; error += CString(CStringA(buf, dw)); SecureZeroMemory(&buf,sizeof(buf)); } WaitForSingleObject(pi.hProcess, INFINITE); GetExitCodeProcess(pi.hProcess, &exitcode); } CloseHandle(pi.hProcess); DeleteFile(szOutput); DeleteFile(szErr); return exitcode; }