|
// Copyright (C) 2011 Fog Creek Software
// Copyright (C) 2009 Benjamin Pollack
// Copyright (C) 2009 Adrian Buehlmann
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdafx.h"
#include "QueryDirstate.h"
#include "Dirstate.h"
#include "DirectoryStatus.h"
#include "Dirstatecache.h"
#include "Winstat.h"
#include "TortoiseUtils.h"
#include "THgStatus.h"
class CQueryState
{
public:
CString strPath;
bool bIsDir;
CString strBaseDir;
CString strHgRoot;
char chStatus;
unsigned uTickCount;
CQueryState() : bIsDir(false), chStatus('0'), uTickCount(0) {}
};
bool HasHgDir(char chClass, const CString& strPath, unsigned& uTicks)
{
uTicks = 0;
bool bRes = false;
if (strPath.IsEmpty() || strPath == "\\") return bRes;
CString strHgPath = strPath + "\\.hg";
if (::PathIsUNCServerShare(strHgPath)) return bRes;
unsigned tc0 = ::GetTickCount();
bRes = ::PathIsDirectory(strHgPath) != 0;
unsigned tc1 = ::GetTickCount();
uTicks = tc1 - tc0;
if (uTicks > 5 /* ms */)
{
// trace slower PathIsDirectory calls (untypical on local discs)
ATLTRACE("[%c] HasHgDir: PathIsDirectory('%s') -> %d, in %d ticks\n",
chClass, (LPCTSTR)strHgPath, (int)bRes, uTicks);
}
return bRes;
}
int FindHgRoot(char chClass, CQueryState& qsCur, CQueryState& qsLast, bool bOutdated)
{
CString dp = "["; dp += chClass; dp += "] findHgRoot";
{
CString strPath = qsCur.strPath;
strPath.AppendChar('\\');
bool bUnset = false;
if (strPath.Find("\\.hg\\") != -1)
{
// ignore files and dirs named '.hg'
qsLast = qsCur;
return 0;
}
int nPos;
if ((nPos = strPath.Find("\\.kbf\\")) != -1)
{
strPath.Truncate(nPos);
strPath += "\\.hg\\kilnbfiles\\dirstate";
CWinstat stat;
if (stat.lstat(strPath, true) == 0)
{
// ignore files and dirs named '.kbf' when kbfiles is enabled
qsLast = qsCur;
return 0;
}
}
}
if (!bOutdated && !qsLast.strHgRoot.IsEmpty()
&& qsCur.strPath.GetLength() >= qsLast.strHgRoot.GetLength()
&& StartsWith(qsCur.strPath, qsLast.strHgRoot + "\\"))
{
qsCur.strHgRoot = qsLast.strHgRoot;
return 1;
}
unsigned uTicks = 0;
#ifdef _DEBUG
bool bFileAccessIsUnacceptablySlow = false;
#endif
if (!::PathIsNetworkPath(qsCur.strPath))
{
// checking if we have a repo root, visible from its parent dir
bool bHasHg = HasHgDir(chClass, qsCur.strPath, uTicks);
if (uTicks > 5000 /* ms */)
{
#ifdef _DEBUG
bFileAccessIsUnacceptablySlow = true;
#endif
goto exit;
}
if (bHasHg)
{
qsCur.strHgRoot = qsCur.strPath;
ATLTRACE("%s('%s'): hgroot = cur.path\n", (LPCTSTR)dp, (LPCTSTR)qsCur.strPath);
return 1;
}
}
qsCur.strBaseDir = DirName(qsCur.strPath);
if (!bOutdated && !qsLast.strBaseDir.IsEmpty() && qsCur.strBaseDir == qsLast.strBaseDir)
{
qsCur.strHgRoot = qsLast.strHgRoot;
return 1;
}
for (CString strPath = qsCur.strBaseDir;;)
{
bool bHasHg = HasHgDir(chClass, strPath, uTicks);
if (uTicks > 5000 /* ms */)
{
CString strReason = "ignoring slow \"" + strPath + "\"";
CTHgStatus::Error(strReason);
#ifdef _DEBUG
bFileAccessIsUnacceptablySlow = true;
#endif
goto exit;
}
if (bHasHg)
{
qsCur.strHgRoot = strPath;
ATLTRACE("%s('%s'): hgroot = '%s' (found repo)", (LPCTSTR)dp,
(LPCTSTR)qsCur.strPath, (LPCTSTR)qsCur.strHgRoot);
return 1;
}
CString strPath2 = DirName(strPath);
if (strPath2.GetLength() == strPath.GetLength())
break;
strPath = strPath2;
}
exit:
#ifdef _DEBUG
if (bFileAccessIsUnacceptablySlow)
{
ATLTRACE("****** %s('%s'): ignored, call took too long (%d ticks)\n",
(LPCTSTR)dp, (LPCTSTR)qsCur.strPath, uTicks);
}
else
{
ATLTRACE("%s('%s'): NO repo found\n", (LPCTSTR)dp, (LPCTSTR)qsCur.strPath);
}
#endif
qsLast = qsCur;
return 0;
}
int GetRelPath(const CString& strHgRoot, const CString& strPath, CString& strRes)
{
int nOffset = strHgRoot.GetLength();
if (nOffset == 0) return 0;
if (nOffset > strPath.GetLength()) return 0;
if (strPath[nOffset] == '\\') nOffset++;
strRes = strPath.Mid(nOffset);
return 1;
}
int GetHgRoot(const CString &strPath, CString& strHgRoot)
{
static CQueryState qsLast;
if (strPath.IsEmpty()) return 0;
CQueryState qsCur;
qsCur.strPath = strPath;
qsCur.uTickCount = ::GetTickCount();
bool bOutdated = qsCur.uTickCount - qsLast.uTickCount > 2000;
if (!bOutdated && qsLast.strPath == strPath)
{
strHgRoot = qsLast.strHgRoot;
return 1;
}
if (::PathIsRoot(strPath))
{
qsLast = qsCur;
return 0;
}
if (FindHgRoot('0', qsCur, qsLast, bOutdated) == 0) return 0;
if (qsCur.strHgRoot.IsEmpty()) return 0;
strHgRoot = qsCur.strHgRoot;
return 1;
}
int HgQueryDirstate(char chClass, const CString& strPath, char chFilterStatus,
char& chOutStatus)
{
CString dp = "["; dp += chClass; dp += "] HgQueryDirstate: ";
static CQueryState qsLast;
if (strPath.IsEmpty()) return 0;
CQueryState qsCur;
qsCur.strPath = strPath;
qsCur.uTickCount = ::GetTickCount();
bool bOutdated = qsCur.uTickCount - qsLast.uTickCount > 2000;
if (!bOutdated && qsLast.strPath == strPath)
{
chOutStatus = qsLast.chStatus;
if (chOutStatus == 'P')
chOutStatus = 'M';
return 1;
}
if (::PathIsRoot(strPath))
{
qsLast = qsCur;
return 0;
}
if (FindHgRoot(chClass, qsCur, qsLast, bOutdated) == 0) return 0;
int nOffset = qsCur.strHgRoot.GetLength();
if (nOffset == 0)
{
qsLast = qsCur;
return 0;
}
if (strPath[nOffset] == '\\')
nOffset++;
CString strRelPath = strPath.Mid(nOffset);
for (int i = 0; i < strRelPath.GetLength(); ++i)
{
if (strRelPath[i] == '\\') strRelPath.SetAt(i, '/');
}
CDirectoryStatus* pDirStatus = CDirectoryStatus::Get(qsCur.strHgRoot, qsCur.strBaseDir);
if (pDirStatus && pDirStatus->NoIcons())
{
qsLast = qsCur;
return 0;
}
if (strRelPath.IsEmpty())
{
chOutStatus = (pDirStatus ? pDirStatus->Status(strRelPath) : '?');
qsCur.chStatus = chOutStatus;
qsCur.uTickCount = ::GetTickCount();
qsLast = qsCur;
return 1;
}
bool bUnset = false;
CDirstate* pDirstate = CDirstateCache::Get(qsCur.strHgRoot, qsCur.strBaseDir, bUnset);
if (!pDirstate)
{
ATLTRACE("%s Dirstatecache::Get('%s') returns no Dirstate\n", (LPCTSTR)dp,
(LPCTSTR)qsCur.strHgRoot);
qsLast = qsCur;
return 0;
}
CWinstat stat;
if (0 != stat.lstat(strPath))
{
ATLTRACE("%s lstat('%s') failed", (LPCTSTR)dp, (LPCTSTR)strPath);
qsLast = qsCur;
return 0;
}
qsCur.bIsDir = stat.bIsDir;
#if 0
ATLTRACE("%s stat.lstat('%s') -> stat.bIsDir is %d\n" << (LPCTSTR)dp,
(LPCTSTR)qsCur.strPath, (int)stat.bIsDir);
#endif
if (qsCur.bIsDir)
{
if (!strRelPath.IsEmpty() && !pDirstate->Root().GetDir(strRelPath))
{
// attempt to get status from kbfiles
pDirstate = CDirstateCache::Get(qsCur.strHgRoot, qsCur.strBaseDir, bUnset, true);
if (!pDirstate || !pDirstate->Root().GetDir(strRelPath))
{
qsLast = qsCur;
return 0; // unknown dir -> no icon
}
}
chOutStatus = (pDirStatus ? pDirStatus->Status(strRelPath) : '?');
qsCur.chStatus = chOutStatus;
qsCur.uTickCount = ::GetTickCount();
qsLast = qsCur;
return 1;
}
const CDirentry* e = pDirstate->Root().Get(strRelPath);
if (!e)
{
// attempt to get status from kbfiles
pDirstate = CDirstateCache::Get(qsCur.strHgRoot, qsCur.strBaseDir, bUnset, true);
if (pDirstate)
{
e = pDirstate->Root().Get(strRelPath);
}
if (!e)
{
qsLast = qsCur;
return 0;
}
}
chOutStatus = e->Status(stat);
if (bUnset) goto exit;
bool bUpdate = false;
if (chOutStatus == 'M')
{
CString strRelBase;
if (pDirStatus && GetRelPath(qsCur.strHgRoot, qsCur.strBaseDir, strRelBase))
{
ATLTRACE("%s strRelBase = '%s'\n", (LPCTSTR)dp, (LPCTSTR)strRelBase);
char chBaseDirStatus = pDirStatus->Status(strRelBase);
ATLTRACE("%s chBaseDirStatus = '%c'\n", (LPCTSTR)dp, chBaseDirStatus);
if (chBaseDirStatus != 'M') bUpdate = true;
}
}
else if (chOutStatus == 'P')
{
static unsigned uLastTickCount;
unsigned tc = ::GetTickCount();
bool bOutdated = tc - uLastTickCount > 6000;
if (bOutdated) // protect against endless update loops
{
bUpdate = true;
uLastTickCount = tc;
}
ATLTRACE("%s chOutStatus is 'P'\n", (LPCTSTR)dp);
}
if (bUpdate)
{
ATLTRACE("%s calling CTHgStatus::Update\n", (LPCTSTR)dp);
CTHgStatus::Update(strPath);
}
exit:
qsCur.chStatus = chOutStatus;
if (chOutStatus == 'P')
chOutStatus = 'M';
qsCur.uTickCount = ::GetTickCount();
qsLast = qsCur;
return 1;
}
|
Loading...