Extend static functions in CAppUtils with a window handle parameter
[TortoiseGit.git] / src / TortoiseShell / ShellCache.cpp
blobb1bd74de7ef97a9267e6d849a38c19a079744fe0
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2017 - TortoiseGit
4 // Copyright (C) 2003-2017 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "ShellCache.h"
22 #include "GitAdminDir.h"
24 ShellCache::ShellCache()
26 cachetype = CRegStdDWORD(L"Software\\TortoiseGit\\CacheType", GetSystemMetrics(SM_REMOTESESSION) ? dll : exe, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
27 onlynonelevated = CRegStdDWORD(L"Software\\TortoiseGit\\ShowOverlaysOnlyNonElevated", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
28 showrecursive = CRegStdDWORD(L"Software\\TortoiseGit\\RecursiveOverlay", TRUE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
29 folderoverlay = CRegStdDWORD(L"Software\\TortoiseGit\\FolderOverlay", TRUE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
30 driveremote = CRegStdDWORD(L"Software\\TortoiseGit\\DriveMaskRemote", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
31 drivefixed = CRegStdDWORD(L"Software\\TortoiseGit\\DriveMaskFixed", TRUE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
32 drivecdrom = CRegStdDWORD(L"Software\\TortoiseGit\\DriveMaskCDROM", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
33 driveremove = CRegStdDWORD(L"Software\\TortoiseGit\\DriveMaskRemovable", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
34 drivefloppy = CRegStdDWORD(L"Software\\TortoiseGit\\DriveMaskFloppy", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
35 driveram = CRegStdDWORD(L"Software\\TortoiseGit\\DriveMaskRAM", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
36 driveunknown = CRegStdDWORD(L"Software\\TortoiseGit\\DriveMaskUnknown", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
37 shellmenuaccelerators = CRegStdDWORD(L"Software\\TortoiseGit\\ShellMenuAccelerators", TRUE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
38 simplecontext = CRegStdDWORD(L"Software\\TortoiseGit\\SimpleContext", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
39 unversionedasmodified = CRegStdDWORD(L"Software\\TortoiseGit\\UnversionedAsModified", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
40 recursesubmodules = CRegStdDWORD(L"Software\\TortoiseGit\\TGitCacheRecurseSubmodules", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
41 hidemenusforunversioneditems = CRegStdDWORD(L"Software\\TortoiseGit\\HideMenusForUnversionedItems", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
42 showunversionedoverlay = CRegStdDWORD(L"Software\\TortoiseGit\\ShowUnversionedOverlay", TRUE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
43 showignoredoverlay = CRegStdDWORD(L"Software\\TortoiseGit\\ShowIgnoredOverlay", TRUE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
44 getlocktop = CRegStdDWORD(L"Software\\TortoiseGit\\GetLockTop", TRUE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
45 excludedasnormal = CRegStdDWORD(L"Software\\TortoiseGit\\ShowExcludedAsNormal", TRUE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
46 drivetypeticker = 0;
48 unsigned __int64 entries = (DEFAULTMENUTOPENTRIES);
49 menulayoutlow = CRegStdDWORD(L"Software\\TortoiseGit\\ContextMenuEntries", entries & 0xFFFFFFFF, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
50 menulayouthigh = CRegStdDWORD(L"Software\\TortoiseGit\\ContextMenuEntrieshigh", entries >> 32, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
52 unsigned __int64 ext = (DEFAULTMENUEXTENTRIES);
53 menuextlow = CRegStdDWORD(L"Software\\TortoiseGit\\ContextMenuExtEntriesLow", ext & 0xFFFFFFFF, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
54 menuexthigh = CRegStdDWORD(L"Software\\TortoiseGit\\ContextMenuExtEntriesHigh", ext >> 32, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
56 menumasklow_lm = CRegStdDWORD(L"Software\\TortoiseGit\\ContextMenuEntriesMaskLow", 0, FALSE, HKEY_LOCAL_MACHINE, KEY_WOW64_64KEY);
57 menumaskhigh_lm = CRegStdDWORD(L"Software\\TortoiseGit\\ContextMenuEntriesMaskHigh", 0, FALSE, HKEY_LOCAL_MACHINE, KEY_WOW64_64KEY);
58 menumasklow_cu = CRegStdDWORD(L"Software\\TortoiseGit\\ContextMenuEntriesMaskLow", 0, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
59 menumaskhigh_cu = CRegStdDWORD(L"Software\\TortoiseGit\\ContextMenuEntriesMaskHigh", 0, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
60 menumaskticker = 0;
61 langid = CRegStdDWORD(L"Software\\TortoiseGit\\LanguageID", 1033, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
62 blockstatus = CRegStdDWORD(L"Software\\TortoiseGit\\BlockStatus", 0, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
63 std::fill_n(drivetypecache, 27, (UINT)-1);
64 if (DWORD(drivefloppy) == 0)
66 // A: and B: are floppy disks
67 drivetypecache[0] = DRIVE_REMOVABLE;
68 drivetypecache[1] = DRIVE_REMOVABLE;
70 drivetypepathcache[0] = L'\0';
71 nocontextpaths = CRegStdString(L"Software\\TortoiseGit\\NoContextPaths", L"", false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
72 m_critSec.Init();
73 // Use RegNotifyChangeKeyValue() to get a notification event whenever a registry value
74 // below HKCU\Software\TortoiseGit is changed. If a value has changed, re-read all
75 // the registry variables to ensure we use the latest ones
76 RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\TortoiseGit", 0, KEY_NOTIFY | KEY_WOW64_64KEY, &m_hNotifyRegKey);
77 m_registryChangeEvent = CreateEvent(nullptr, true, false, nullptr);
78 if (RegNotifyChangeKeyValue(m_hNotifyRegKey, false, REG_NOTIFY_CHANGE_LAST_SET, m_registryChangeEvent, TRUE) != ERROR_SUCCESS)
80 if (m_registryChangeEvent)
81 CloseHandle(m_registryChangeEvent);
82 m_registryChangeEvent = nullptr;
83 RegCloseKey(m_hNotifyRegKey);
84 m_hNotifyRegKey = nullptr;
87 // find out if we're elevated
88 isElevated = false;
89 HANDLE hToken = nullptr;
90 if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken))
92 TOKEN_ELEVATION te = { 0 };
93 DWORD dwReturnLength = 0;
94 if (::GetTokenInformation(hToken, TokenElevation, &te, sizeof(te), &dwReturnLength))
95 isElevated = (te.TokenIsElevated != 0);
96 ::CloseHandle(hToken);
100 ShellCache::~ShellCache()
102 if (m_registryChangeEvent)
103 CloseHandle(m_registryChangeEvent);
104 m_registryChangeEvent = nullptr;
105 if (m_hNotifyRegKey)
106 RegCloseKey(m_hNotifyRegKey);
107 m_hNotifyRegKey = nullptr;
110 bool ShellCache::RefreshIfNeeded()
112 // don't wait for the registry change event but only test if such an event
113 // has occurred since the last time we got here.
114 // if the event has occurred, re-read all registry variables and of course
115 // re-set the notification event to get further notifications of registry changes.
116 bool signalled = WaitForSingleObjectEx(m_registryChangeEvent, 0, true) != WAIT_TIMEOUT;
117 if (!signalled)
118 return signalled;
120 if (RegNotifyChangeKeyValue(m_hNotifyRegKey, false, REG_NOTIFY_CHANGE_LAST_SET, m_registryChangeEvent, TRUE) != ERROR_SUCCESS)
122 CloseHandle(m_registryChangeEvent);
123 m_registryChangeEvent = nullptr;
124 RegCloseKey(m_hNotifyRegKey);
125 m_hNotifyRegKey = nullptr;
128 cachetype.read();
129 onlynonelevated.read();
130 showrecursive.read();
131 folderoverlay.read();
132 driveremote.read();
133 drivefixed.read();
134 drivecdrom.read();
135 driveremove.read();
136 drivefloppy.read();
137 driveram.read();
138 driveunknown.read();
139 simplecontext.read();
140 shellmenuaccelerators.read();
141 unversionedasmodified.read();
142 recursesubmodules.read();
143 showunversionedoverlay.read();
144 showignoredoverlay.read();
145 excludedasnormal.read();
146 hidemenusforunversioneditems.read();
147 menulayoutlow.read();
148 menulayouthigh.read();
149 langid.read();
150 blockstatus.read();
151 getlocktop.read();
152 menumasklow_lm.read();
153 menumaskhigh_lm.read();
154 menumasklow_cu.read();
155 menumaskhigh_cu.read();
156 nocontextpaths.read();
158 Locker lock(m_critSec);
159 pathFilter.Refresh();
161 return signalled;
164 ShellCache::CacheType ShellCache::GetCacheType()
166 RefreshIfNeeded();
167 return CacheType(DWORD((cachetype)));
170 DWORD ShellCache::BlockStatus()
172 RefreshIfNeeded();
173 return (blockstatus);
176 unsigned __int64 ShellCache::GetMenuLayout()
178 RefreshIfNeeded();
179 unsigned __int64 temp = unsigned __int64(DWORD(menulayouthigh)) << 32;
180 temp |= unsigned __int64(DWORD(menulayoutlow));
181 return temp;
184 unsigned __int64 ShellCache::GetMenuExt()
186 RefreshIfNeeded();
187 unsigned __int64 temp = unsigned __int64(DWORD(menuexthigh)) << 32;
188 temp |= unsigned __int64(DWORD(menuextlow));
189 return temp;
192 unsigned __int64 ShellCache::GetMenuMask()
194 auto ticks = GetTickCount64();
195 if ((ticks - menumaskticker) > ADMINDIRTIMEOUT)
197 menumaskticker = ticks;
198 menumasklow_lm.read();
199 menumaskhigh_lm.read();
201 DWORD low = (DWORD)menumasklow_lm | (DWORD)menumasklow_cu;
202 DWORD high = (DWORD)menumaskhigh_lm | (DWORD)menumaskhigh_cu;
203 unsigned __int64 temp = unsigned __int64(high) << 32;
204 temp |= unsigned __int64(low);
205 return temp;
208 bool ShellCache::IsProcessElevated()
210 return isElevated;
213 BOOL ShellCache::IsOnlyNonElevated()
215 RefreshIfNeeded();
216 return (onlynonelevated);
219 BOOL ShellCache::IsRecursive()
221 RefreshIfNeeded();
222 return (showrecursive);
225 BOOL ShellCache::IsFolderOverlay()
227 RefreshIfNeeded();
228 return (folderoverlay);
231 BOOL ShellCache::IsSimpleContext()
233 RefreshIfNeeded();
234 return (simplecontext != 0);
237 BOOL ShellCache::HasShellMenuAccelerators()
239 RefreshIfNeeded();
240 return (shellmenuaccelerators != 0);
243 BOOL ShellCache::IsUnversionedAsModified()
245 RefreshIfNeeded();
246 return (unversionedasmodified);
249 BOOL ShellCache::IsRecurseSubmodules()
251 RefreshIfNeeded();
252 return (recursesubmodules);
255 BOOL ShellCache::ShowUnversionedOverlay()
257 RefreshIfNeeded();
258 return (showunversionedoverlay);
261 BOOL ShellCache::ShowIgnoredOverlay()
263 RefreshIfNeeded();
264 return (showignoredoverlay);
267 BOOL ShellCache::IsGetLockTop()
269 RefreshIfNeeded();
270 return (getlocktop);
273 BOOL ShellCache::ShowExcludedAsNormal()
275 RefreshIfNeeded();
276 return (excludedasnormal);
279 BOOL ShellCache::HideMenusForUnversionedItems()
281 RefreshIfNeeded();
282 return (hidemenusforunversioneditems);
285 BOOL ShellCache::IsRemote()
287 RefreshIfNeeded();
288 return (driveremote);
291 BOOL ShellCache::IsFixed()
293 RefreshIfNeeded();
294 return (drivefixed);
297 BOOL ShellCache::IsCDRom()
299 RefreshIfNeeded();
300 return (drivecdrom);
303 BOOL ShellCache::IsRemovable()
305 RefreshIfNeeded();
306 return (driveremove);
309 BOOL ShellCache::IsRAM()
311 RefreshIfNeeded();
312 return (driveram);
315 BOOL ShellCache::IsUnknown()
317 RefreshIfNeeded();
318 return (driveunknown);
321 BOOL ShellCache::IsContextPathAllowed(LPCTSTR path)
323 Locker lock(m_critSec);
324 ExcludeContextValid();
325 for (const auto& exPath : excontextvector)
327 if (exPath.empty())
328 continue;
329 if (exPath[exPath.size() - 1] == '*')
331 tstring str = exPath.substr(0, exPath.size() - 1);
332 if (_wcsnicmp(str.c_str(), path, str.size()) == 0)
333 return FALSE;
335 else if (_wcsicmp(exPath.c_str(), path) == 0)
336 return FALSE;
338 return TRUE;
341 BOOL ShellCache::IsPathAllowed(LPCTSTR path)
343 RefreshIfNeeded();
344 Locker lock(m_critSec);
345 tristate_t allowed = pathFilter.IsPathAllowed(path);
346 if (allowed != tristate_unknown)
347 return allowed == tristate_true ? TRUE : FALSE;
349 UINT drivetype = 0;
350 int drivenumber = PathGetDriveNumber(path);
351 if ((drivenumber >= 0) && (drivenumber < 25))
353 drivetype = drivetypecache[drivenumber];
354 if ((drivetype == -1) || ((GetTickCount64() - drivetypeticker) > DRIVETYPETIMEOUT))
356 if ((DWORD(drivefloppy) == 0) && ((drivenumber == 0) || (drivenumber == 1)))
357 drivetypecache[drivenumber] = DRIVE_REMOVABLE;
358 else
360 drivetypeticker = GetTickCount64();
361 TCHAR pathbuf[MAX_PATH + 4] = { 0 }; // MAX_PATH ok here. PathStripToRoot works with partial paths too.
362 wcsncpy_s(pathbuf, path, _countof(pathbuf) - 1);
363 PathStripToRoot(pathbuf);
364 PathAddBackslash(pathbuf);
365 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": GetDriveType for %s, Drive %d\n", pathbuf, drivenumber);
366 drivetype = GetDriveType(pathbuf);
367 drivetypecache[drivenumber] = drivetype;
371 else
373 TCHAR pathbuf[MAX_PATH + 4] = { 0 }; // MAX_PATH ok here. PathIsUNCServer works with partial paths too.
374 wcsncpy_s(pathbuf, path, _countof(pathbuf) - 1);
375 if (PathIsUNCServer(pathbuf))
376 drivetype = DRIVE_REMOTE;
377 else
379 PathStripToRoot(pathbuf);
380 PathAddBackslash(pathbuf);
381 if (wcsncmp(pathbuf, drivetypepathcache, MAX_PATH - 1) == 0) // MAX_PATH ok.
382 drivetype = drivetypecache[26];
383 else
385 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L"GetDriveType for %s\n", pathbuf);
386 drivetype = GetDriveType(pathbuf);
387 drivetypecache[26] = drivetype;
388 wcsncpy_s(drivetypepathcache, pathbuf, MAX_PATH - 1); // MAX_PATH ok.
392 if ((drivetype == DRIVE_REMOVABLE) && (!IsRemovable()))
393 return FALSE;
394 if ((drivetype == DRIVE_FIXED) && (!IsFixed()))
395 return FALSE;
396 if (((drivetype == DRIVE_REMOTE) || (drivetype == DRIVE_NO_ROOT_DIR)) && (!IsRemote()))
397 return FALSE;
398 if ((drivetype == DRIVE_CDROM) && (!IsCDRom()))
399 return FALSE;
400 if ((drivetype == DRIVE_RAMDISK) && (!IsRAM()))
401 return FALSE;
402 if ((drivetype == DRIVE_UNKNOWN) && (IsUnknown()))
403 return FALSE;
405 return TRUE;
408 DWORD ShellCache::GetLangID()
410 RefreshIfNeeded();
411 return (langid);
414 BOOL ShellCache::HasGITAdminDir(LPCTSTR path, BOOL bIsDir, CString* ProjectTopDir /*= nullptr*/)
416 tstring folder(path);
417 if (!bIsDir)
419 size_t pos = folder.rfind(L'\\');
420 if (pos != tstring::npos)
421 folder.erase(pos);
423 std::map<tstring, AdminDir_s>::const_iterator iter;
424 if ((iter = admindircache.find(folder)) != admindircache.cend())
426 Locker lock(m_critSec);
427 if ((GetTickCount64() - iter->second.timeout) < ADMINDIRTIMEOUT)
429 if (ProjectTopDir && iter->second.bHasAdminDir)
430 *ProjectTopDir = iter->second.sProjectRoot.c_str();
431 return iter->second.bHasAdminDir;
435 CString sProjectRoot;
436 BOOL hasAdminDir = GitAdminDir::HasAdminDir(folder.c_str(), true, &sProjectRoot);
438 Locker lock(m_critSec);
439 AdminDir_s& ad = admindircache[folder];
440 ad.bHasAdminDir = hasAdminDir;
441 ad.timeout = GetTickCount64();
442 if (hasAdminDir)
444 ad.sProjectRoot.assign(sProjectRoot);
445 if (ProjectTopDir)
446 *ProjectTopDir = sProjectRoot;
449 return hasAdminDir;
452 void ShellCache::ExcludeContextValid()
454 if (RefreshIfNeeded())
456 Locker lock(m_critSec);
457 if (excludecontextstr.compare((tstring)nocontextpaths) == 0)
458 return;
459 excludecontextstr = (tstring)nocontextpaths;
460 excontextvector.clear();
461 size_t pos = 0, pos_ant = 0;
462 pos = excludecontextstr.find(L'\n', pos_ant);
463 while (pos != tstring::npos)
465 tstring token = excludecontextstr.substr(pos_ant, pos - pos_ant);
466 excontextvector.push_back(token);
467 pos_ant = pos + 1;
468 pos = excludecontextstr.find(L'\n', pos_ant);
470 if (!excludecontextstr.empty())
471 excontextvector.push_back(excludecontextstr.substr(pos_ant, excludecontextstr.size() - 1));
472 excludecontextstr = (tstring)nocontextpaths;
476 // construct \ref data content
477 void ShellCache::CPathFilter::AddEntry(const tstring& s, bool include)
479 static wchar_t pathbuf[MAX_PATH * 4] = { 0 };
480 if (s.empty())
481 return;
483 TCHAR lastChar = *s.rbegin();
485 SEntry entry;
486 entry.hasSubFolderEntries = false;
487 entry.recursive = lastChar != L'?';
488 entry.included = include ? tristate_true : tristate_false;
489 entry.subPathIncluded = include == entry.recursive ? tristate_true : tristate_false;
491 entry.path = s;
492 if ((lastChar == L'?') || (lastChar == L'*'))
493 entry.path.erase(s.length() - 1);
494 if (!entry.path.empty() && (*entry.path.rbegin() == L'\\'))
495 entry.path.erase(entry.path.length() - 1);
497 auto ret = ExpandEnvironmentStrings(entry.path.c_str(), pathbuf, _countof(pathbuf));
498 if ((ret > 0) && (ret < _countof(pathbuf)))
499 entry.path = pathbuf;
501 data.push_back(entry);
504 void ShellCache::CPathFilter::AddEntries(const tstring& s, bool include)
506 size_t pos = 0, pos_ant = 0;
507 pos = s.find(L'\n', pos_ant);
508 while (pos != tstring::npos)
510 AddEntry(s.substr(pos_ant, pos - pos_ant), include);
511 pos_ant = pos + 1;
512 pos = s.find(L'\n', pos_ant);
515 if (!s.empty())
516 AddEntry(s.substr(pos_ant, s.size() - 1), include);
519 // for all paths, have at least one entry in data
520 void ShellCache::CPathFilter::PostProcessData()
522 if (data.empty())
523 return;
525 std::sort(data.begin(), data.end());
527 // update subPathIncluded props and remove duplicate entries
528 auto begin = data.begin();
529 auto end = data.end();
530 auto dest = begin;
531 for (auto source = begin; source != end; ++source)
533 if (_wcsicmp(source->path.c_str(), dest->path.c_str()) == 0)
535 // multiple entries for the same path -> merge them
537 // update subPathIncluded
538 // (all relevant parent info has already been normalized)
539 if (!source->recursive)
540 source->subPathIncluded = IsPathAllowed(source->path.c_str(), begin, dest);
542 // multiple specs for the same path
543 // -> merge them into the existing entry @ dest
544 if (!source->recursive && dest->recursive)
546 // reset the marker for the this case
547 dest->recursive = false;
548 dest->included = source->included;
550 else
552 // include beats exclude
553 if (source->included == tristate_true)
554 dest->included = tristate_true;
555 if (source->recursive && source->subPathIncluded == tristate_true)
556 dest->subPathIncluded = tristate_true;
559 else
561 // new path -> don't merge this entry
562 size_t destSize = dest->path.size();
563 dest->hasSubFolderEntries = (source->path.size() > destSize) && (source->path[destSize] == L'\\') && (_wcsnicmp(source->path.substr(0, destSize).c_str(), dest->path.c_str(), destSize) == 0);
565 *++dest = *source;
567 // update subPathIncluded
568 // (all relevant parent info has already been normalized)
569 if (!dest->recursive)
570 dest->subPathIncluded = IsPathAllowed(source->path.c_str(), begin, dest);
574 // remove duplicate info
575 if (begin != end)
576 data.erase(++dest, end);
579 // lookup. default result is "unknown".
580 // We must look for *every* parent path because of situations like:
581 // excluded: C:, C:\some\deep\path
582 // include: C:\some
583 // lookup for C:\some\deeper\path
584 tristate_t ShellCache::CPathFilter::IsPathAllowed(LPCTSTR path, TData::const_iterator begin, TData::const_iterator end) const
586 tristate_t result = tristate_unknown;
588 // handle special cases
589 if (begin == end)
590 return result;
592 size_t maxLength = wcslen(path);
593 if (maxLength == 0)
594 return result;
596 // look for the most specific entry, start at the root
597 size_t pos = 0;
600 LPCTSTR backslash = wcschr(path + pos + 1,L'\\');
601 pos = backslash == nullptr ? maxLength : backslash - path;
603 std::pair<LPCTSTR, size_t> toFind(path, pos);
604 TData::const_iterator iter = std::lower_bound(begin, end, toFind);
606 // found a relevant entry?
607 if ((iter != end) && (iter->path.length() == pos) && (_wcsnicmp(iter->path.c_str(), path, pos) == 0))
609 // exact match?
610 if (pos == maxLength)
611 return iter->included;
613 // parent match
614 result = iter->subPathIncluded;
616 // done?
617 if (iter->hasSubFolderEntries)
618 begin = iter;
619 else
620 return result;
622 else
624 // set a (potentially) closer lower limit
625 if (iter != begin)
626 begin = --iter;
629 // set a (potentially) closer upper limit
630 end = std::upper_bound(begin, end, toFind);
631 } while ((pos < maxLength) && (begin != end));
633 // nothing more specific found
634 return result;
637 // construction
639 ShellCache::CPathFilter::CPathFilter()
640 : excludelist(L"Software\\TortoiseGit\\OverlayExcludeList", L"", false, HKEY_CURRENT_USER, KEY_WOW64_64KEY)
641 , includelist(L"Software\\TortoiseGit\\OverlayIncludeList", L"", false, HKEY_CURRENT_USER, KEY_WOW64_64KEY)
643 Refresh();
646 // notify of (potential) registry settings
648 void ShellCache::CPathFilter::Refresh()
650 excludelist.read();
651 includelist.read();
653 if ((excludeliststr.compare((tstring)excludelist) == 0) && (includeliststr.compare((tstring)includelist) == 0))
654 return;
656 excludeliststr = (tstring)excludelist;
657 includeliststr = (tstring)includelist;
658 data.clear();
659 AddEntries(excludeliststr, false);
660 AddEntries(includeliststr, true);
662 PostProcessData();
665 // data access
666 tristate_t ShellCache::CPathFilter::IsPathAllowed(LPCTSTR path) const
668 if (!path)
669 return tristate_unknown;
670 // always ignore the recycle bin
671 PTSTR pFound = StrStrI(path, L":\\RECYCLER");
672 if (pFound)
674 if ((*(pFound + 10) == L'\0') || (*(pFound + 10) == L'\\'))
675 return tristate_false;
677 pFound = StrStrI(path, L":\\$Recycle.Bin");
678 if (pFound)
680 if ((*(pFound + 14) == '\0') || (*(pFound + 14) == L'\\'))
681 return tristate_false;
683 return IsPathAllowed(path, data.begin(), data.end());