Updated to add local changes ignored files when updating status list
[TortoiseGit.git] / src / TortoiseShell / ShellCache.cpp
blobae8c4ace43d6f4a49774bda976534c1d1927dc5e
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2018 - 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 excludedasnormal = CRegStdDWORD(L"Software\\TortoiseGit\\ShowExcludedAsNormal", TRUE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
45 drivetypeticker = 0;
47 unsigned __int64 entries = (DEFAULTMENUTOPENTRIES);
48 menulayoutlow = CRegStdDWORD(L"Software\\TortoiseGit\\ContextMenuEntries", entries & 0xFFFFFFFF, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
49 menulayouthigh = CRegStdDWORD(L"Software\\TortoiseGit\\ContextMenuEntrieshigh", entries >> 32, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
51 unsigned __int64 ext = (DEFAULTMENUEXTENTRIES);
52 menuextlow = CRegStdDWORD(L"Software\\TortoiseGit\\ContextMenuExtEntriesLow", ext & 0xFFFFFFFF, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
53 menuexthigh = CRegStdDWORD(L"Software\\TortoiseGit\\ContextMenuExtEntriesHigh", ext >> 32, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
55 menumasklow_lm = CRegStdDWORD(L"Software\\TortoiseGit\\ContextMenuEntriesMaskLow", 0, FALSE, HKEY_LOCAL_MACHINE, KEY_WOW64_64KEY);
56 menumaskhigh_lm = CRegStdDWORD(L"Software\\TortoiseGit\\ContextMenuEntriesMaskHigh", 0, FALSE, HKEY_LOCAL_MACHINE, KEY_WOW64_64KEY);
57 menumasklow_cu = CRegStdDWORD(L"Software\\TortoiseGit\\ContextMenuEntriesMaskLow", 0, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
58 menumaskhigh_cu = CRegStdDWORD(L"Software\\TortoiseGit\\ContextMenuEntriesMaskHigh", 0, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
59 menumaskticker = 0;
60 langid = CRegStdDWORD(L"Software\\TortoiseGit\\LanguageID", 1033, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
61 blockstatus = CRegStdDWORD(L"Software\\TortoiseGit\\BlockStatus", 0, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
62 std::fill_n(drivetypecache, 27, (UINT)-1);
63 if (DWORD(drivefloppy) == 0)
65 // A: and B: are floppy disks
66 drivetypecache[0] = DRIVE_REMOVABLE;
67 drivetypecache[1] = DRIVE_REMOVABLE;
69 drivetypepathcache[0] = L'\0';
70 nocontextpaths = CRegStdString(L"Software\\TortoiseGit\\NoContextPaths", L"", false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
71 m_critSec.Init();
72 // Use RegNotifyChangeKeyValue() to get a notification event whenever a registry value
73 // below HKCU\Software\TortoiseGit is changed. If a value has changed, re-read all
74 // the registry variables to ensure we use the latest ones
75 RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\TortoiseGit", 0, KEY_NOTIFY | KEY_WOW64_64KEY, &m_hNotifyRegKey);
76 m_registryChangeEvent = CreateEvent(nullptr, true, false, nullptr);
77 if (RegNotifyChangeKeyValue(m_hNotifyRegKey, false, REG_NOTIFY_CHANGE_LAST_SET, m_registryChangeEvent, TRUE) != ERROR_SUCCESS)
79 if (m_registryChangeEvent)
80 CloseHandle(m_registryChangeEvent);
81 m_registryChangeEvent = nullptr;
82 RegCloseKey(m_hNotifyRegKey);
83 m_hNotifyRegKey = nullptr;
86 // find out if we're elevated
87 isElevated = false;
88 HANDLE hToken = nullptr;
89 if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken))
91 TOKEN_ELEVATION te = { 0 };
92 DWORD dwReturnLength = 0;
93 if (::GetTokenInformation(hToken, TokenElevation, &te, sizeof(te), &dwReturnLength))
94 isElevated = (te.TokenIsElevated != 0);
95 ::CloseHandle(hToken);
99 ShellCache::~ShellCache()
101 if (m_registryChangeEvent)
102 CloseHandle(m_registryChangeEvent);
103 m_registryChangeEvent = nullptr;
104 if (m_hNotifyRegKey)
105 RegCloseKey(m_hNotifyRegKey);
106 m_hNotifyRegKey = nullptr;
109 bool ShellCache::RefreshIfNeeded()
111 // don't wait for the registry change event but only test if such an event
112 // has occurred since the last time we got here.
113 // if the event has occurred, re-read all registry variables and of course
114 // re-set the notification event to get further notifications of registry changes.
115 bool signalled = WaitForSingleObjectEx(m_registryChangeEvent, 0, true) != WAIT_TIMEOUT;
116 if (!signalled)
117 return signalled;
119 if (RegNotifyChangeKeyValue(m_hNotifyRegKey, false, REG_NOTIFY_CHANGE_LAST_SET, m_registryChangeEvent, TRUE) != ERROR_SUCCESS)
121 CloseHandle(m_registryChangeEvent);
122 m_registryChangeEvent = nullptr;
123 RegCloseKey(m_hNotifyRegKey);
124 m_hNotifyRegKey = nullptr;
127 cachetype.read();
128 onlynonelevated.read();
129 showrecursive.read();
130 folderoverlay.read();
131 driveremote.read();
132 drivefixed.read();
133 drivecdrom.read();
134 driveremove.read();
135 drivefloppy.read();
136 driveram.read();
137 driveunknown.read();
138 simplecontext.read();
139 shellmenuaccelerators.read();
140 unversionedasmodified.read();
141 recursesubmodules.read();
142 showunversionedoverlay.read();
143 showignoredoverlay.read();
144 excludedasnormal.read();
145 hidemenusforunversioneditems.read();
146 menulayoutlow.read();
147 menulayouthigh.read();
148 langid.read();
149 blockstatus.read();
150 menumasklow_lm.read();
151 menumaskhigh_lm.read();
152 menumasklow_cu.read();
153 menumaskhigh_cu.read();
154 menuextlow.read();
155 menuexthigh.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::ShowExcludedAsNormal()
269 RefreshIfNeeded();
270 return (excludedasnormal);
273 BOOL ShellCache::HideMenusForUnversionedItems()
275 RefreshIfNeeded();
276 return (hidemenusforunversioneditems);
279 BOOL ShellCache::IsRemote()
281 RefreshIfNeeded();
282 return (driveremote);
285 BOOL ShellCache::IsFixed()
287 RefreshIfNeeded();
288 return (drivefixed);
291 BOOL ShellCache::IsCDRom()
293 RefreshIfNeeded();
294 return (drivecdrom);
297 BOOL ShellCache::IsRemovable()
299 RefreshIfNeeded();
300 return (driveremove);
303 BOOL ShellCache::IsRAM()
305 RefreshIfNeeded();
306 return (driveram);
309 BOOL ShellCache::IsUnknown()
311 RefreshIfNeeded();
312 return (driveunknown);
315 BOOL ShellCache::IsContextPathAllowed(LPCTSTR path)
317 Locker lock(m_critSec);
318 ExcludeContextValid();
319 for (const auto& exPath : excontextvector)
321 if (exPath.empty())
322 continue;
323 if (exPath[exPath.size() - 1] == '*')
325 tstring str = exPath.substr(0, exPath.size() - 1);
326 if (_wcsnicmp(str.c_str(), path, str.size()) == 0)
327 return FALSE;
329 else if (_wcsicmp(exPath.c_str(), path) == 0)
330 return FALSE;
332 return TRUE;
335 BOOL ShellCache::IsPathAllowed(LPCTSTR path)
337 RefreshIfNeeded();
338 Locker lock(m_critSec);
339 tristate_t allowed = pathFilter.IsPathAllowed(path);
340 if (allowed != tristate_unknown)
341 return allowed == tristate_true ? TRUE : FALSE;
343 UINT drivetype = 0;
344 int drivenumber = PathGetDriveNumber(path);
345 if ((drivenumber >= 0) && (drivenumber < 25))
347 drivetype = drivetypecache[drivenumber];
348 if ((drivetype == -1) || ((GetTickCount64() - drivetypeticker) > DRIVETYPETIMEOUT))
350 if ((DWORD(drivefloppy) == 0) && ((drivenumber == 0) || (drivenumber == 1)))
351 drivetypecache[drivenumber] = DRIVE_REMOVABLE;
352 else
354 drivetypeticker = GetTickCount64();
355 TCHAR pathbuf[MAX_PATH + 4] = { 0 }; // MAX_PATH ok here. PathStripToRoot works with partial paths too.
356 wcsncpy_s(pathbuf, path, _countof(pathbuf) - 1);
357 PathStripToRoot(pathbuf);
358 PathAddBackslash(pathbuf);
359 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": GetDriveType for %s, Drive %d\n", pathbuf, drivenumber);
360 drivetype = GetDriveType(pathbuf);
361 drivetypecache[drivenumber] = drivetype;
365 else
367 TCHAR pathbuf[MAX_PATH + 4] = { 0 }; // MAX_PATH ok here. PathIsUNCServer works with partial paths too.
368 wcsncpy_s(pathbuf, path, _countof(pathbuf) - 1);
369 if (PathIsUNCServer(pathbuf))
370 drivetype = DRIVE_REMOTE;
371 else
373 PathStripToRoot(pathbuf);
374 PathAddBackslash(pathbuf);
375 if (wcsncmp(pathbuf, drivetypepathcache, MAX_PATH - 1) == 0) // MAX_PATH ok.
376 drivetype = drivetypecache[26];
377 else
379 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L"GetDriveType for %s\n", pathbuf);
380 drivetype = GetDriveType(pathbuf);
381 drivetypecache[26] = drivetype;
382 wcsncpy_s(drivetypepathcache, pathbuf, MAX_PATH - 1); // MAX_PATH ok.
386 if ((drivetype == DRIVE_REMOVABLE) && (!IsRemovable()))
387 return FALSE;
388 if ((drivetype == DRIVE_FIXED) && (!IsFixed()))
389 return FALSE;
390 if (((drivetype == DRIVE_REMOTE) || (drivetype == DRIVE_NO_ROOT_DIR)) && (!IsRemote()))
391 return FALSE;
392 if ((drivetype == DRIVE_CDROM) && (!IsCDRom()))
393 return FALSE;
394 if ((drivetype == DRIVE_RAMDISK) && (!IsRAM()))
395 return FALSE;
396 if ((drivetype == DRIVE_UNKNOWN) && (IsUnknown()))
397 return FALSE;
399 return TRUE;
402 DWORD ShellCache::GetLangID()
404 RefreshIfNeeded();
405 return (langid);
408 BOOL ShellCache::HasGITAdminDir(LPCTSTR path, BOOL bIsDir, CString* ProjectTopDir /*= nullptr*/)
410 tstring folder(path);
411 if (!bIsDir)
413 size_t pos = folder.rfind(L'\\');
414 if (pos != tstring::npos)
415 folder.erase(pos);
417 std::map<tstring, AdminDir_s>::const_iterator iter;
418 if ((iter = admindircache.find(folder)) != admindircache.cend())
420 Locker lock(m_critSec);
421 if ((GetTickCount64() - iter->second.timeout) < ADMINDIRTIMEOUT)
423 if (ProjectTopDir && iter->second.bHasAdminDir)
424 *ProjectTopDir = iter->second.sProjectRoot.c_str();
425 return iter->second.bHasAdminDir;
429 CString sProjectRoot;
430 BOOL hasAdminDir = GitAdminDir::HasAdminDir(folder.c_str(), true, &sProjectRoot);
432 Locker lock(m_critSec);
433 AdminDir_s& ad = admindircache[folder];
434 ad.bHasAdminDir = hasAdminDir;
435 ad.timeout = GetTickCount64();
436 if (hasAdminDir)
438 ad.sProjectRoot.assign(sProjectRoot);
439 if (ProjectTopDir)
440 *ProjectTopDir = sProjectRoot;
443 return hasAdminDir;
446 void ShellCache::ExcludeContextValid()
448 if (RefreshIfNeeded())
450 Locker lock(m_critSec);
451 if (excludecontextstr.compare((tstring)nocontextpaths) == 0)
452 return;
453 excludecontextstr = (tstring)nocontextpaths;
454 excontextvector.clear();
455 size_t pos = 0, pos_ant = 0;
456 pos = excludecontextstr.find(L'\n', pos_ant);
457 while (pos != tstring::npos)
459 tstring token = excludecontextstr.substr(pos_ant, pos - pos_ant);
460 excontextvector.push_back(token);
461 pos_ant = pos + 1;
462 pos = excludecontextstr.find(L'\n', pos_ant);
464 if (!excludecontextstr.empty())
465 excontextvector.push_back(excludecontextstr.substr(pos_ant, excludecontextstr.size() - 1));
466 excludecontextstr = (tstring)nocontextpaths;
470 // construct \ref data content
471 void ShellCache::CPathFilter::AddEntry(const tstring& s, bool include)
473 static wchar_t pathbuf[MAX_PATH * 4] = { 0 };
474 if (s.empty())
475 return;
477 TCHAR lastChar = *s.rbegin();
479 SEntry entry;
480 entry.hasSubFolderEntries = false;
481 entry.recursive = lastChar != L'?';
482 entry.included = include ? tristate_true : tristate_false;
483 entry.subPathIncluded = include == entry.recursive ? tristate_true : tristate_false;
485 entry.path = s;
486 if ((lastChar == L'?') || (lastChar == L'*'))
487 entry.path.erase(s.length() - 1);
488 if (!entry.path.empty() && (*entry.path.rbegin() == L'\\'))
489 entry.path.erase(entry.path.length() - 1);
491 auto ret = ExpandEnvironmentStrings(entry.path.c_str(), pathbuf, _countof(pathbuf));
492 if ((ret > 0) && (ret < _countof(pathbuf)))
493 entry.path = pathbuf;
495 data.push_back(entry);
498 void ShellCache::CPathFilter::AddEntries(const tstring& s, bool include)
500 size_t pos = 0, pos_ant = 0;
501 pos = s.find(L'\n', pos_ant);
502 while (pos != tstring::npos)
504 AddEntry(s.substr(pos_ant, pos - pos_ant), include);
505 pos_ant = pos + 1;
506 pos = s.find(L'\n', pos_ant);
509 if (!s.empty())
510 AddEntry(s.substr(pos_ant, s.size() - 1), include);
513 // for all paths, have at least one entry in data
514 void ShellCache::CPathFilter::PostProcessData()
516 if (data.empty())
517 return;
519 std::sort(data.begin(), data.end());
521 // update subPathIncluded props and remove duplicate entries
522 auto begin = data.begin();
523 auto end = data.end();
524 auto dest = begin;
525 for (auto source = begin; source != end; ++source)
527 if (_wcsicmp(source->path.c_str(), dest->path.c_str()) == 0)
529 // multiple entries for the same path -> merge them
531 // update subPathIncluded
532 // (all relevant parent info has already been normalized)
533 if (!source->recursive)
534 source->subPathIncluded = IsPathAllowed(source->path.c_str(), begin, dest);
536 // multiple specs for the same path
537 // -> merge them into the existing entry @ dest
538 if (!source->recursive && dest->recursive)
540 // reset the marker for the this case
541 dest->recursive = false;
542 dest->included = source->included;
544 else
546 // include beats exclude
547 if (source->included == tristate_true)
548 dest->included = tristate_true;
549 if (source->recursive && source->subPathIncluded == tristate_true)
550 dest->subPathIncluded = tristate_true;
553 else
555 // new path -> don't merge this entry
556 size_t destSize = dest->path.size();
557 dest->hasSubFolderEntries = (source->path.size() > destSize) && (source->path[destSize] == L'\\') && (_wcsnicmp(source->path.substr(0, destSize).c_str(), dest->path.c_str(), destSize) == 0);
559 *++dest = *source;
561 // update subPathIncluded
562 // (all relevant parent info has already been normalized)
563 if (!dest->recursive)
564 dest->subPathIncluded = IsPathAllowed(source->path.c_str(), begin, dest);
568 // remove duplicate info
569 if (begin != end)
570 data.erase(++dest, end);
573 // lookup. default result is "unknown".
574 // We must look for *every* parent path because of situations like:
575 // excluded: C:, C:\some\deep\path
576 // include: C:\some
577 // lookup for C:\some\deeper\path
578 tristate_t ShellCache::CPathFilter::IsPathAllowed(LPCTSTR path, TData::const_iterator begin, TData::const_iterator end) const
580 tristate_t result = tristate_unknown;
582 // handle special cases
583 if (begin == end)
584 return result;
586 size_t maxLength = wcslen(path);
587 if (maxLength == 0)
588 return result;
590 // look for the most specific entry, start at the root
591 size_t pos = 0;
594 LPCTSTR backslash = wcschr(path + pos + 1,L'\\');
595 pos = backslash == nullptr ? maxLength : backslash - path;
597 std::pair<LPCTSTR, size_t> toFind(path, pos);
598 TData::const_iterator iter = std::lower_bound(begin, end, toFind);
600 // found a relevant entry?
601 if ((iter != end) && (iter->path.length() == pos) && (_wcsnicmp(iter->path.c_str(), path, pos) == 0))
603 // exact match?
604 if (pos == maxLength)
605 return iter->included;
607 // parent match
608 result = iter->subPathIncluded;
610 // done?
611 if (iter->hasSubFolderEntries)
612 begin = iter;
613 else
614 return result;
616 else
618 // set a (potentially) closer lower limit
619 if (iter != begin)
620 begin = --iter;
623 // set a (potentially) closer upper limit
624 end = std::upper_bound(begin, end, toFind);
625 } while ((pos < maxLength) && (begin != end));
627 // nothing more specific found
628 return result;
631 // construction
633 ShellCache::CPathFilter::CPathFilter()
634 : excludelist(L"Software\\TortoiseGit\\OverlayExcludeList", L"", false, HKEY_CURRENT_USER, KEY_WOW64_64KEY)
635 , includelist(L"Software\\TortoiseGit\\OverlayIncludeList", L"", false, HKEY_CURRENT_USER, KEY_WOW64_64KEY)
637 Refresh();
640 // notify of (potential) registry settings
642 void ShellCache::CPathFilter::Refresh()
644 excludelist.read();
645 includelist.read();
647 if ((excludeliststr.compare((tstring)excludelist) == 0) && (includeliststr.compare((tstring)includelist) == 0))
648 return;
650 excludeliststr = (tstring)excludelist;
651 includeliststr = (tstring)includelist;
652 data.clear();
653 AddEntries(excludeliststr, false);
654 AddEntries(includeliststr, true);
656 PostProcessData();
659 // data access
660 tristate_t ShellCache::CPathFilter::IsPathAllowed(LPCTSTR path) const
662 if (!path)
663 return tristate_unknown;
664 // always ignore the recycle bin
665 PTSTR pFound = StrStrI(path, L":\\RECYCLER");
666 if (pFound)
668 if ((*(pFound + 10) == L'\0') || (*(pFound + 10) == L'\\'))
669 return tristate_false;
671 pFound = StrStrI(path, L":\\$Recycle.Bin");
672 if (pFound)
674 if ((*(pFound + 14) == '\0') || (*(pFound + 14) == L'\\'))
675 return tristate_false;
677 return IsPathAllowed(path, data.begin(), data.end());