Apply backgroundcolors.patch
[TortoiseGit.git] / src / TortoiseShell / ShellCache.cpp
blob1202d1e78fe9121ab1ef8d635c4713dad90779e7
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2023 - 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);
46 menuLayout11 = CRegStdQWORD(L"Software\\TortoiseGit\\ContextMenu11Entries", DEFAULTWIN11MENUTOPENTRIES, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
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 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 nocontextpaths = CRegStdString(L"Software\\TortoiseGit\\NoContextPaths", L"", false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
70 // Use RegNotifyChangeKeyValue() to get a notification event whenever a registry value
71 // below HKCU\Software\TortoiseGit is changed. If a value has changed, re-read all
72 // the registry variables to ensure we use the latest ones
73 RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\TortoiseGit", 0, KEY_NOTIFY | KEY_WOW64_64KEY, &m_hNotifyRegKey);
74 m_registryChangeEvent = CreateEvent(nullptr, true, false, nullptr);
75 if (RegNotifyChangeKeyValue(m_hNotifyRegKey, false, REG_NOTIFY_CHANGE_LAST_SET, m_registryChangeEvent, TRUE) != ERROR_SUCCESS)
77 if (m_registryChangeEvent)
78 CloseHandle(m_registryChangeEvent);
79 m_registryChangeEvent = nullptr;
80 RegCloseKey(m_hNotifyRegKey);
81 m_hNotifyRegKey = nullptr;
84 // find out if we're elevated
85 HANDLE hToken = nullptr;
86 if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken))
88 TOKEN_ELEVATION te = { 0 };
89 DWORD dwReturnLength = 0;
90 if (::GetTokenInformation(hToken, TokenElevation, &te, sizeof(te), &dwReturnLength))
91 isElevated = (te.TokenIsElevated != 0);
92 ::CloseHandle(hToken);
96 ShellCache::~ShellCache()
98 if (m_registryChangeEvent)
99 CloseHandle(m_registryChangeEvent);
100 m_registryChangeEvent = nullptr;
101 if (m_hNotifyRegKey)
102 RegCloseKey(m_hNotifyRegKey);
103 m_hNotifyRegKey = nullptr;
106 bool ShellCache::RefreshIfNeeded()
108 // don't wait for the registry change event but only test if such an event
109 // has occurred since the last time we got here.
110 // if the event has occurred, re-read all registry variables and of course
111 // re-set the notification event to get further notifications of registry changes.
112 const bool signalled = WaitForSingleObjectEx(m_registryChangeEvent, 0, true) != WAIT_TIMEOUT;
113 if (!signalled)
114 return signalled;
116 if (RegNotifyChangeKeyValue(m_hNotifyRegKey, false, REG_NOTIFY_CHANGE_LAST_SET, m_registryChangeEvent, TRUE) != ERROR_SUCCESS)
118 CloseHandle(m_registryChangeEvent);
119 m_registryChangeEvent = nullptr;
120 RegCloseKey(m_hNotifyRegKey);
121 m_hNotifyRegKey = nullptr;
124 cachetype.read();
125 onlynonelevated.read();
126 showrecursive.read();
127 folderoverlay.read();
128 driveremote.read();
129 drivefixed.read();
130 drivecdrom.read();
131 driveremove.read();
132 drivefloppy.read();
133 driveram.read();
134 driveunknown.read();
135 simplecontext.read();
136 shellmenuaccelerators.read();
137 unversionedasmodified.read();
138 recursesubmodules.read();
139 showunversionedoverlay.read();
140 showignoredoverlay.read();
141 excludedasnormal.read();
142 hidemenusforunversioneditems.read();
143 menuLayout11.read();
144 menulayoutlow.read();
145 menulayouthigh.read();
146 langid.read();
147 blockstatus.read();
148 menumasklow_lm.read();
149 menumaskhigh_lm.read();
150 menumasklow_cu.read();
151 menumaskhigh_cu.read();
152 menuextlow.read();
153 menuexthigh.read();
154 nocontextpaths.read();
156 if (DWORD(drivefloppy) == 0)
158 // A: and B: are floppy disks
159 drivetypecache[0] = DRIVE_REMOVABLE;
160 drivetypecache[1] = DRIVE_REMOVABLE;
162 else
164 // reset floppy drive cache
165 drivetypecache[0] = UINT(-1);
166 drivetypecache[1] = UINT(-1);
169 Locker lock(m_critSec);
170 pathFilter.Refresh();
172 return signalled;
175 ShellCache::CacheType ShellCache::GetCacheType()
177 RefreshIfNeeded();
178 return CacheType(static_cast<DWORD>(cachetype));
181 DWORD ShellCache::BlockStatus()
183 RefreshIfNeeded();
184 return (blockstatus);
187 unsigned __int64 ShellCache::GetMenuLayout11()
189 RefreshIfNeeded();
190 return menuLayout11;
193 unsigned __int64 ShellCache::GetMenuLayout()
195 RefreshIfNeeded();
196 ULARGE_INTEGER temp;
197 temp.HighPart = menulayouthigh;
198 temp.LowPart = menulayoutlow;
199 return temp.QuadPart;
202 unsigned __int64 ShellCache::GetMenuExt()
204 RefreshIfNeeded();
205 ULARGE_INTEGER temp;
206 temp.HighPart = menuexthigh;
207 temp.LowPart = menuextlow;
208 return temp.QuadPart;
211 unsigned __int64 ShellCache::GetMenuMask()
213 auto ticks = GetTickCount64();
214 if ((ticks - menumaskticker) > ADMINDIRTIMEOUT)
216 menumaskticker = ticks;
217 menumasklow_lm.read();
218 menumaskhigh_lm.read();
221 ULARGE_INTEGER temp;
222 temp.LowPart = menumasklow_lm | menumasklow_cu;
223 temp.HighPart = menumaskhigh_lm | menumaskhigh_cu;
224 return temp.QuadPart;
227 bool ShellCache::IsProcessElevated()
229 return isElevated;
232 BOOL ShellCache::IsOnlyNonElevated()
234 RefreshIfNeeded();
235 return (onlynonelevated);
238 BOOL ShellCache::IsRecursive()
240 RefreshIfNeeded();
241 return (showrecursive);
244 BOOL ShellCache::IsFolderOverlay()
246 RefreshIfNeeded();
247 return (folderoverlay);
250 BOOL ShellCache::IsSimpleContext()
252 RefreshIfNeeded();
253 return (simplecontext != 0);
256 BOOL ShellCache::HasShellMenuAccelerators()
258 RefreshIfNeeded();
259 return (shellmenuaccelerators != 0);
262 BOOL ShellCache::IsUnversionedAsModified()
264 RefreshIfNeeded();
265 return (unversionedasmodified);
268 BOOL ShellCache::IsRecurseSubmodules()
270 RefreshIfNeeded();
271 return (recursesubmodules);
274 BOOL ShellCache::ShowUnversionedOverlay()
276 RefreshIfNeeded();
277 return (showunversionedoverlay);
280 BOOL ShellCache::ShowIgnoredOverlay()
282 RefreshIfNeeded();
283 return (showignoredoverlay);
286 BOOL ShellCache::ShowExcludedAsNormal()
288 RefreshIfNeeded();
289 return (excludedasnormal);
292 BOOL ShellCache::HideMenusForUnversionedItems()
294 RefreshIfNeeded();
295 return (hidemenusforunversioneditems);
298 BOOL ShellCache::IsRemote()
300 RefreshIfNeeded();
301 return (driveremote);
304 BOOL ShellCache::IsFixed()
306 RefreshIfNeeded();
307 return (drivefixed);
310 BOOL ShellCache::IsCDRom()
312 RefreshIfNeeded();
313 return (drivecdrom);
316 BOOL ShellCache::IsRemovable()
318 RefreshIfNeeded();
319 return (driveremove);
322 BOOL ShellCache::IsRAM()
324 RefreshIfNeeded();
325 return (driveram);
328 BOOL ShellCache::IsUnknown()
330 RefreshIfNeeded();
331 return (driveunknown);
334 BOOL ShellCache::IsContextPathAllowed(LPCWSTR path)
336 Locker lock(m_critSec);
337 ExcludeContextValid();
338 for (const auto& exPath : excontextvector)
340 if (exPath.empty())
341 continue;
342 if (exPath[exPath.size() - 1] == '*')
344 std::wstring str = exPath.substr(0, exPath.size() - 1);
345 if (_wcsnicmp(str.c_str(), path, str.size()) == 0)
346 return FALSE;
348 else if (_wcsicmp(exPath.c_str(), path) == 0)
349 return FALSE;
351 return TRUE;
354 BOOL ShellCache::IsPathAllowed(LPCWSTR path)
356 RefreshIfNeeded();
357 Locker lock(m_critSec);
358 const Tristate allowed = pathFilter.IsPathAllowed(path);
359 if (allowed != Tristate::Unknown)
360 return allowed == Tristate::True ? TRUE : FALSE;
362 UINT drivetype = 0;
363 const int drivenumber = PathGetDriveNumber(path);
364 if ((drivenumber >= 0) && (drivenumber <= 25))
366 drivetype = drivetypecache[drivenumber];
367 if ((drivetype == -1) || ((GetTickCount64() - drivetypeticker) > DRIVETYPETIMEOUT))
369 if ((DWORD(drivefloppy) == 0) && ((drivenumber == 0) || (drivenumber == 1)))
370 drivetype = DRIVE_REMOVABLE;
371 else if (PathIsNetworkPath(path))
372 drivetype = DRIVE_REMOTE;
373 else
375 wchar_t pathbuf[MAX_PATH + 4] = { 0 }; // MAX_PATH ok here. PathStripToRoot works with partial paths too.
376 wcsncpy_s(pathbuf, path, _countof(pathbuf) - 1);
377 PathStripToRoot(pathbuf);
378 PathAddBackslash(pathbuf);
379 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": GetDriveType for %s, Drive %d\n", pathbuf, drivenumber);
380 drivetype = GetDriveType(pathbuf);
382 drivetypecache[drivenumber] = drivetype;
383 drivetypeticker = GetTickCount64();
386 else
388 if (PathIsNetworkPath(path))
389 drivetype = DRIVE_REMOTE;
390 else
392 wchar_t pathbuf[MAX_PATH + 4] = { 0 }; // MAX_PATH ok here. PathStripToRoot works with partial paths too.
393 wcsncpy_s(pathbuf, path, _countof(pathbuf) - 1);
394 PathStripToRoot(pathbuf);
395 PathAddBackslash(pathbuf);
396 if (wcsncmp(pathbuf, drivetypepathcache, MAX_PATH - 1) == 0) // MAX_PATH ok.
397 drivetype = drivetypecache[26];
398 else
400 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L"GetDriveType for %s\n", pathbuf);
401 drivetype = GetDriveType(pathbuf);
402 drivetypecache[26] = drivetype;
403 wcsncpy_s(drivetypepathcache, pathbuf, MAX_PATH - 1); // MAX_PATH ok.
407 if ((drivetype == DRIVE_REMOVABLE) && (!IsRemovable()))
408 return FALSE;
409 if ((drivetype == DRIVE_FIXED) && (!IsFixed()))
410 return FALSE;
411 if (((drivetype == DRIVE_REMOTE) || (drivetype == DRIVE_NO_ROOT_DIR)) && (!IsRemote()))
412 return FALSE;
413 if ((drivetype == DRIVE_CDROM) && (!IsCDRom()))
414 return FALSE;
415 if ((drivetype == DRIVE_RAMDISK) && (!IsRAM()))
416 return FALSE;
417 if ((drivetype == DRIVE_UNKNOWN) && (IsUnknown()))
418 return FALSE;
420 return TRUE;
423 DWORD ShellCache::GetLangID()
425 RefreshIfNeeded();
426 return (langid);
429 BOOL ShellCache::HasGITAdminDir(LPCWSTR path, BOOL bIsDir, CString* ProjectTopDir /*= nullptr*/)
431 std::wstring folder(path);
432 if (!bIsDir)
434 size_t pos = folder.rfind(L'\\');
435 if (pos != std::wstring::npos)
436 folder.erase(pos);
438 std::map<std::wstring, AdminDir_s>::const_iterator iter;
439 if ((iter = admindircache.find(folder)) != admindircache.cend())
441 Locker lock(m_critSec);
442 if ((GetTickCount64() - iter->second.timeout) < ADMINDIRTIMEOUT)
444 if (ProjectTopDir && iter->second.bHasAdminDir)
445 *ProjectTopDir = iter->second.sProjectRoot.c_str();
446 return iter->second.bHasAdminDir;
450 CString sProjectRoot;
451 const BOOL hasAdminDir = GitAdminDir::HasAdminDir(folder.c_str(), true, &sProjectRoot);
453 Locker lock(m_critSec);
454 AdminDir_s& ad = admindircache[folder];
455 ad.bHasAdminDir = hasAdminDir;
456 ad.timeout = GetTickCount64();
457 if (hasAdminDir)
459 ad.sProjectRoot.assign(sProjectRoot);
460 if (ProjectTopDir)
461 *ProjectTopDir = sProjectRoot;
464 return hasAdminDir;
467 void ShellCache::ExcludeContextValid()
469 // Lock must be taken by caller, which is done by IsContextPathAllowed()
470 RefreshIfNeeded();
471 if (excludecontextstr.compare(nocontextpaths) == 0)
472 return;
474 excludecontextstr = nocontextpaths;
475 excontextvector.clear();
476 size_t pos = 0, pos_ant = 0;
477 pos = excludecontextstr.find(L'\n', pos_ant);
478 while (pos != std::wstring::npos)
480 std::wstring token = excludecontextstr.substr(pos_ant, pos - pos_ant);
481 if (!token.empty())
482 excontextvector.push_back(token);
483 pos_ant = pos + 1;
484 pos = excludecontextstr.find(L'\n', pos_ant);
486 if (!excludecontextstr.empty())
488 std::wstring token = excludecontextstr.substr(pos_ant, excludecontextstr.size() - 1);
489 if (!token.empty())
490 excontextvector.push_back(token);
494 // construct \ref data content
495 void ShellCache::CPathFilter::AddEntry(const std::wstring& s, bool include)
497 static wchar_t pathbuf[MAX_PATH * 4] = { 0 };
498 if (s.empty())
499 return;
501 wchar_t lastChar = *s.rbegin();
503 SEntry entry;
504 entry.hasSubFolderEntries = false;
505 entry.recursive = lastChar != L'?';
506 entry.included = include ? Tristate::True : Tristate::False;
507 entry.subPathIncluded = include == entry.recursive ? Tristate::True : Tristate::False;
509 entry.path = s;
510 if ((lastChar == L'?') || (lastChar == L'*'))
511 entry.path.erase(s.length() - 1);
512 if (!entry.path.empty() && (*entry.path.rbegin() == L'\\'))
513 entry.path.erase(entry.path.length() - 1);
515 if (auto ret = ExpandEnvironmentStrings(entry.path.c_str(), pathbuf, _countof(pathbuf)); ret > 0 && ret < _countof(pathbuf))
516 entry.path = pathbuf;
518 data.push_back(entry);
521 void ShellCache::CPathFilter::AddEntries(const std::wstring& s, bool include)
523 size_t pos = 0, pos_ant = 0;
524 pos = s.find(L'\n', pos_ant);
525 while (pos != std::wstring::npos)
527 AddEntry(s.substr(pos_ant, pos - pos_ant), include);
528 pos_ant = pos + 1;
529 pos = s.find(L'\n', pos_ant);
532 if (!s.empty())
533 AddEntry(s.substr(pos_ant, s.size() - 1), include);
536 // for all paths, have at least one entry in data
537 void ShellCache::CPathFilter::PostProcessData()
539 if (data.empty())
540 return;
542 std::sort(data.begin(), data.end());
544 // update subPathIncluded props and remove duplicate entries
545 auto begin = data.begin();
546 auto end = data.end();
547 auto dest = begin;
548 for (auto source = begin; source != end; ++source)
550 if (_wcsicmp(source->path.c_str(), dest->path.c_str()) == 0)
552 // multiple entries for the same path -> merge them
554 // update subPathIncluded
555 // (all relevant parent info has already been normalized)
556 if (!source->recursive)
557 source->subPathIncluded = IsPathAllowed(source->path.c_str(), begin, dest);
559 // multiple specs for the same path
560 // -> merge them into the existing entry @ dest
561 if (!source->recursive && dest->recursive)
563 // reset the marker for the this case
564 dest->recursive = false;
565 dest->included = source->included;
567 else
569 // include beats exclude
570 if (source->included == Tristate::True)
571 dest->included = Tristate::True;
572 if (source->recursive && source->subPathIncluded == Tristate::True)
573 dest->subPathIncluded = Tristate::True;
576 else
578 // new path -> don't merge this entry
579 size_t destSize = dest->path.size();
580 dest->hasSubFolderEntries = (source->path.size() > destSize) && (source->path[destSize] == L'\\') && (_wcsnicmp(source->path.substr(0, destSize).c_str(), dest->path.c_str(), destSize) == 0);
582 *++dest = *source;
584 // update subPathIncluded
585 // (all relevant parent info has already been normalized)
586 if (!dest->recursive)
587 dest->subPathIncluded = IsPathAllowed(source->path.c_str(), begin, dest);
591 // remove duplicate info
592 if (begin != end)
593 data.erase(++dest, end);
596 // lookup. default result is "unknown".
597 // We must look for *every* parent path because of situations like:
598 // excluded: C:, C:\some\deep\path
599 // include: C:\some
600 // lookup for C:\some\deeper\path
601 Tristate ShellCache::CPathFilter::IsPathAllowed(LPCWSTR path, TData::const_iterator begin, TData::const_iterator end) const
603 Tristate result = Tristate::Unknown;
605 // handle special cases
606 if (begin == end)
607 return result;
609 size_t maxLength = wcslen(path);
610 if (maxLength == 0)
611 return result;
613 // look for the most specific entry, start at the root
614 size_t pos = 0;
617 LPCWSTR backslash = wcschr(path + pos + 1,L'\\');
618 pos = backslash == nullptr ? maxLength : backslash - path;
620 std::pair<LPCWSTR, size_t> toFind(path, pos);
621 TData::const_iterator iter = std::lower_bound(begin, end, toFind);
623 // found a relevant entry?
624 if ((iter != end) && (iter->path.length() == pos) && (_wcsnicmp(iter->path.c_str(), path, pos) == 0))
626 // exact match?
627 if (pos == maxLength)
628 return iter->included;
630 // parent match
631 result = iter->subPathIncluded;
633 // done?
634 if (iter->hasSubFolderEntries)
635 begin = iter;
636 else
637 return result;
639 else
641 // set a (potentially) closer lower limit
642 if (iter != begin)
643 begin = --iter;
646 // set a (potentially) closer upper limit
647 end = std::upper_bound(begin, end, toFind);
648 } while ((pos < maxLength) && (begin != end));
650 // nothing more specific found
651 return result;
654 // construction
656 ShellCache::CPathFilter::CPathFilter()
657 : excludelist(L"Software\\TortoiseGit\\OverlayExcludeList", L"", false, HKEY_CURRENT_USER, KEY_WOW64_64KEY)
658 , includelist(L"Software\\TortoiseGit\\OverlayIncludeList", L"", false, HKEY_CURRENT_USER, KEY_WOW64_64KEY)
660 Refresh();
663 // notify of (potential) registry settings
665 void ShellCache::CPathFilter::Refresh()
667 excludelist.read();
668 includelist.read();
670 if (excludeliststr.compare(excludelist) == 0 && includeliststr.compare(includelist) == 0)
671 return;
673 excludeliststr = excludelist;
674 includeliststr = includelist;
675 data.clear();
676 AddEntries(excludeliststr, false);
677 AddEntries(includeliststr, true);
679 PostProcessData();
682 // data access
683 Tristate ShellCache::CPathFilter::IsPathAllowed(LPCWSTR path) const
685 if (!path)
686 return Tristate::Unknown;
687 // always ignore the recycle bin
688 PTSTR pFound = StrStrI(path, L":\\RECYCLER");
689 if (pFound)
691 if ((*(pFound + 10) == L'\0') || (*(pFound + 10) == L'\\'))
692 return Tristate::False;
694 pFound = StrStrI(path, L":\\$Recycle.Bin");
695 if (pFound)
697 if ((*(pFound + 14) == '\0') || (*(pFound + 14) == L'\\'))
698 return Tristate::False;
700 return IsPathAllowed(path, data.begin(), data.end());