Pass wide chars
[TortoiseGit.git] / src / TortoiseShell / ShellCache.cpp
blob3b0df2189bf6e39f28a1fcf731581914b173a073
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2016 - TortoiseGit
4 // Copyright (C) 2003-2016 - 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 showrecursive = CRegStdDWORD(L"Software\\TortoiseGit\\RecursiveOverlay", TRUE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
28 folderoverlay = CRegStdDWORD(L"Software\\TortoiseGit\\FolderOverlay", TRUE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
29 driveremote = CRegStdDWORD(L"Software\\TortoiseGit\\DriveMaskRemote", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
30 drivefixed = CRegStdDWORD(L"Software\\TortoiseGit\\DriveMaskFixed", TRUE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
31 drivecdrom = CRegStdDWORD(L"Software\\TortoiseGit\\DriveMaskCDROM", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
32 driveremove = CRegStdDWORD(L"Software\\TortoiseGit\\DriveMaskRemovable", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
33 drivefloppy = CRegStdDWORD(L"Software\\TortoiseGit\\DriveMaskFloppy", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
34 driveram = CRegStdDWORD(L"Software\\TortoiseGit\\DriveMaskRAM", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
35 driveunknown = CRegStdDWORD(L"Software\\TortoiseGit\\DriveMaskUnknown", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
36 shellmenuaccelerators = CRegStdDWORD(L"Software\\TortoiseGit\\ShellMenuAccelerators", TRUE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
37 simplecontext = CRegStdDWORD(L"Software\\TortoiseGit\\SimpleContext", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
38 unversionedasmodified = CRegStdDWORD(L"Software\\TortoiseGit\\UnversionedAsModified", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
39 recursesubmodules = CRegStdDWORD(L"Software\\TortoiseGit\\TGitCacheRecurseSubmodules", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
40 hidemenusforunversioneditems = CRegStdDWORD(L"Software\\TortoiseGit\\HideMenusForUnversionedItems", FALSE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
41 showunversionedoverlay = CRegStdDWORD(L"Software\\TortoiseGit\\ShowUnversionedOverlay", TRUE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
42 showignoredoverlay = CRegStdDWORD(L"Software\\TortoiseGit\\ShowIgnoredOverlay", TRUE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY);
43 getlocktop = CRegStdDWORD(L"Software\\TortoiseGit\\GetLockTop", 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 CloseHandle(m_registryChangeEvent);
80 m_registryChangeEvent = nullptr;
81 RegCloseKey(m_hNotifyRegKey);
82 m_hNotifyRegKey = nullptr;
86 ShellCache::~ShellCache()
88 if (m_registryChangeEvent)
89 CloseHandle(m_registryChangeEvent);
90 m_registryChangeEvent = nullptr;
91 if (m_hNotifyRegKey)
92 RegCloseKey(m_hNotifyRegKey);
93 m_hNotifyRegKey = nullptr;
96 bool ShellCache::RefreshIfNeeded()
98 // don't wait for the registry change event but only test if such an event
99 // has occurred since the last time we got here.
100 // if the event has occurred, re-read all registry variables and of course
101 // re-set the notification event to get further notifications of registry changes.
102 bool signalled = WaitForSingleObjectEx(m_registryChangeEvent, 0, true) != WAIT_TIMEOUT;
103 if (!signalled)
104 return signalled;
106 if (RegNotifyChangeKeyValue(m_hNotifyRegKey, false, REG_NOTIFY_CHANGE_LAST_SET, m_registryChangeEvent, TRUE) != ERROR_SUCCESS)
108 CloseHandle(m_registryChangeEvent);
109 m_registryChangeEvent = nullptr;
110 RegCloseKey(m_hNotifyRegKey);
111 m_hNotifyRegKey = nullptr;
114 cachetype.read();
115 showrecursive.read();
116 folderoverlay.read();
117 driveremote.read();
118 drivefixed.read();
119 drivecdrom.read();
120 driveremove.read();
121 drivefloppy.read();
122 driveram.read();
123 driveunknown.read();
124 simplecontext.read();
125 shellmenuaccelerators.read();
126 unversionedasmodified.read();
127 recursesubmodules.read();
128 showunversionedoverlay.read();
129 showignoredoverlay.read();
130 excludedasnormal.read();
131 hidemenusforunversioneditems.read();
132 menulayoutlow.read();
133 menulayouthigh.read();
134 langid.read();
135 blockstatus.read();
136 getlocktop.read();
137 menumasklow_lm.read();
138 menumaskhigh_lm.read();
139 menumasklow_cu.read();
140 menumaskhigh_cu.read();
141 nocontextpaths.read();
143 Locker lock(m_critSec);
144 pathFilter.Refresh();
146 return signalled;
149 ShellCache::CacheType ShellCache::GetCacheType()
151 RefreshIfNeeded();
152 return CacheType(DWORD((cachetype)));
155 DWORD ShellCache::BlockStatus()
157 RefreshIfNeeded();
158 return (blockstatus);
161 unsigned __int64 ShellCache::GetMenuLayout()
163 RefreshIfNeeded();
164 unsigned __int64 temp = unsigned __int64(DWORD(menulayouthigh)) << 32;
165 temp |= unsigned __int64(DWORD(menulayoutlow));
166 return temp;
169 unsigned __int64 ShellCache::GetMenuExt()
171 RefreshIfNeeded();
172 unsigned __int64 temp = unsigned __int64(DWORD(menuexthigh)) << 32;
173 temp |= unsigned __int64(DWORD(menuextlow));
174 return temp;
177 unsigned __int64 ShellCache::GetMenuMask()
179 auto ticks = GetTickCount64();
180 if ((ticks - menumaskticker) > ADMINDIRTIMEOUT)
182 menumaskticker = ticks;
183 menumasklow_lm.read();
184 menumaskhigh_lm.read();
186 DWORD low = (DWORD)menumasklow_lm | (DWORD)menumasklow_cu;
187 DWORD high = (DWORD)menumaskhigh_lm | (DWORD)menumaskhigh_cu;
188 unsigned __int64 temp = unsigned __int64(high) << 32;
189 temp |= unsigned __int64(low);
190 return temp;
193 BOOL ShellCache::IsRecursive()
195 RefreshIfNeeded();
196 return (showrecursive);
199 BOOL ShellCache::IsFolderOverlay()
201 RefreshIfNeeded();
202 return (folderoverlay);
205 BOOL ShellCache::IsSimpleContext()
207 RefreshIfNeeded();
208 return (simplecontext != 0);
211 BOOL ShellCache::HasShellMenuAccelerators()
213 RefreshIfNeeded();
214 return (shellmenuaccelerators != 0);
217 BOOL ShellCache::IsUnversionedAsModified()
219 RefreshIfNeeded();
220 return (unversionedasmodified);
223 BOOL ShellCache::IsRecurseSubmodules()
225 RefreshIfNeeded();
226 return (recursesubmodules);
229 BOOL ShellCache::ShowUnversionedOverlay()
231 RefreshIfNeeded();
232 return (showunversionedoverlay);
235 BOOL ShellCache::ShowIgnoredOverlay()
237 RefreshIfNeeded();
238 return (showignoredoverlay);
241 BOOL ShellCache::IsGetLockTop()
243 RefreshIfNeeded();
244 return (getlocktop);
247 BOOL ShellCache::ShowExcludedAsNormal()
249 RefreshIfNeeded();
250 return (excludedasnormal);
253 BOOL ShellCache::HideMenusForUnversionedItems()
255 RefreshIfNeeded();
256 return (hidemenusforunversioneditems);
259 BOOL ShellCache::IsRemote()
261 RefreshIfNeeded();
262 return (driveremote);
265 BOOL ShellCache::IsFixed()
267 RefreshIfNeeded();
268 return (drivefixed);
271 BOOL ShellCache::IsCDRom()
273 RefreshIfNeeded();
274 return (drivecdrom);
277 BOOL ShellCache::IsRemovable()
279 RefreshIfNeeded();
280 return (driveremove);
283 BOOL ShellCache::IsRAM()
285 RefreshIfNeeded();
286 return (driveram);
289 BOOL ShellCache::IsUnknown()
291 RefreshIfNeeded();
292 return (driveunknown);
295 BOOL ShellCache::IsContextPathAllowed(LPCTSTR path)
297 Locker lock(m_critSec);
298 ExcludeContextValid();
299 for (const auto& exPath : excontextvector)
301 if (exPath.empty())
302 continue;
303 if (exPath.at(exPath.size() - 1) == '*')
305 tstring str = exPath.substr(0, exPath.size() - 1);
306 if (_wcsnicmp(str.c_str(), path, str.size()) == 0)
307 return FALSE;
309 else if (_wcsicmp(exPath.c_str(), path) == 0)
310 return FALSE;
312 return TRUE;
315 BOOL ShellCache::IsPathAllowed(LPCTSTR path)
317 ValidatePathFilter();
318 Locker lock(m_critSec);
319 tristate_t allowed = pathFilter.IsPathAllowed(path);
320 if (allowed != tristate_unknown)
321 return allowed == tristate_true ? TRUE : FALSE;
323 UINT drivetype = 0;
324 int drivenumber = PathGetDriveNumber(path);
325 if ((drivenumber >= 0) && (drivenumber < 25))
327 drivetype = drivetypecache[drivenumber];
328 if ((drivetype == -1) || ((GetTickCount64() - drivetypeticker) > DRIVETYPETIMEOUT))
330 if ((DWORD(drivefloppy) == 0) && ((drivenumber == 0) || (drivenumber == 1)))
331 drivetypecache[drivenumber] = DRIVE_REMOVABLE;
332 else
334 drivetypeticker = GetTickCount64();
335 TCHAR pathbuf[MAX_PATH + 4] = { 0 }; // MAX_PATH ok here. PathStripToRoot works with partial paths too.
336 wcsncpy_s(pathbuf, path, _countof(pathbuf) - 1);
337 PathStripToRoot(pathbuf);
338 PathAddBackslash(pathbuf);
339 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": GetDriveType for %s, Drive %d\n", pathbuf, drivenumber);
340 drivetype = GetDriveType(pathbuf);
341 drivetypecache[drivenumber] = drivetype;
345 else
347 TCHAR pathbuf[MAX_PATH + 4] = { 0 }; // MAX_PATH ok here. PathIsUNCServer works with partial paths too.
348 wcsncpy_s(pathbuf, path, _countof(pathbuf) - 1);
349 if (PathIsUNCServer(pathbuf))
350 drivetype = DRIVE_REMOTE;
351 else
353 PathStripToRoot(pathbuf);
354 PathAddBackslash(pathbuf);
355 if (wcsncmp(pathbuf, drivetypepathcache, MAX_PATH - 1) == 0) // MAX_PATH ok.
356 drivetype = drivetypecache[26];
357 else
359 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L"GetDriveType for %s\n", pathbuf);
360 drivetype = GetDriveType(pathbuf);
361 drivetypecache[26] = drivetype;
362 wcsncpy_s(drivetypepathcache, pathbuf, MAX_PATH - 1); // MAX_PATH ok.
366 if ((drivetype == DRIVE_REMOVABLE) && (!IsRemovable()))
367 return FALSE;
368 if ((drivetype == DRIVE_FIXED) && (!IsFixed()))
369 return FALSE;
370 if (((drivetype == DRIVE_REMOTE) || (drivetype == DRIVE_NO_ROOT_DIR)) && (!IsRemote()))
371 return FALSE;
372 if ((drivetype == DRIVE_CDROM) && (!IsCDRom()))
373 return FALSE;
374 if ((drivetype == DRIVE_RAMDISK) && (!IsRAM()))
375 return FALSE;
376 if ((drivetype == DRIVE_UNKNOWN) && (IsUnknown()))
377 return FALSE;
379 return TRUE;
382 DWORD ShellCache::GetLangID()
384 RefreshIfNeeded();
385 return (langid);
388 BOOL ShellCache::HasGITAdminDir(LPCTSTR path, BOOL bIsDir, CString* ProjectTopDir /*= nullptr*/)
390 tstring folder(path);
391 if (!bIsDir)
393 size_t pos = folder.rfind(L'\\');
394 if (pos != tstring::npos)
395 folder.erase(pos);
397 std::map<tstring, AdminDir_s>::const_iterator iter;
398 if ((iter = admindircache.find(folder)) != admindircache.cend())
400 Locker lock(m_critSec);
401 if ((GetTickCount64() - iter->second.timeout) < ADMINDIRTIMEOUT)
403 if (ProjectTopDir && iter->second.bHasAdminDir)
404 *ProjectTopDir = iter->second.sProjectRoot.c_str();
405 return iter->second.bHasAdminDir;
409 CString sProjectRoot;
410 BOOL hasAdminDir = GitAdminDir::HasAdminDir(folder.c_str(), true, &sProjectRoot);
412 Locker lock(m_critSec);
413 AdminDir_s& ad = admindircache[folder];
414 ad.bHasAdminDir = hasAdminDir;
415 ad.timeout = GetTickCount64();
416 if (hasAdminDir)
418 ad.sProjectRoot.assign(sProjectRoot);
419 if (ProjectTopDir)
420 *ProjectTopDir = sProjectRoot;
423 return hasAdminDir;
426 void ShellCache::ExcludeContextValid()
428 if (RefreshIfNeeded())
430 Locker lock(m_critSec);
431 if (excludecontextstr.compare((tstring)nocontextpaths) == 0)
432 return;
433 excludecontextstr = (tstring)nocontextpaths;
434 excontextvector.clear();
435 size_t pos = 0, pos_ant = 0;
436 pos = excludecontextstr.find(L'\n', pos_ant);
437 while (pos != tstring::npos)
439 tstring token = excludecontextstr.substr(pos_ant, pos - pos_ant);
440 excontextvector.push_back(token);
441 pos_ant = pos + 1;
442 pos = excludecontextstr.find(L'\n', pos_ant);
444 if (!excludecontextstr.empty())
445 excontextvector.push_back(excludecontextstr.substr(pos_ant, excludecontextstr.size() - 1));
446 excludecontextstr = (tstring)nocontextpaths;
450 void ShellCache::ValidatePathFilter()
452 if (RefreshIfNeeded())
454 Locker lock(m_critSec);
455 pathFilter.Refresh();
459 // construct \ref data content
460 void ShellCache::CPathFilter::AddEntry(const tstring& s, bool include)
462 static wchar_t pathbuf[MAX_PATH * 4] = { 0 };
463 if (s.empty())
464 return;
466 TCHAR lastChar = *s.rbegin();
468 SEntry entry;
469 entry.hasSubFolderEntries = false;
470 entry.recursive = lastChar != L'?';
471 entry.included = include ? tristate_true : tristate_false;
472 entry.subPathIncluded = include == entry.recursive ? tristate_true : tristate_false;
474 entry.path = s;
475 if ((lastChar == L'?') || (lastChar == L'*'))
476 entry.path.erase(s.length() - 1);
477 if (!entry.path.empty() && (*entry.path.rbegin() == L'\\'))
478 entry.path.erase(entry.path.length() - 1);
480 auto ret = ExpandEnvironmentStrings(entry.path.c_str(), pathbuf, _countof(pathbuf));
481 if ((ret > 0) && (ret < _countof(pathbuf)))
482 entry.path = pathbuf;
484 data.push_back(entry);
487 void ShellCache::CPathFilter::AddEntries(const tstring& s, bool include)
489 size_t pos = 0, pos_ant = 0;
490 pos = s.find(L'\n', pos_ant);
491 while (pos != tstring::npos)
493 AddEntry(s.substr(pos_ant, pos - pos_ant), include);
494 pos_ant = pos + 1;
495 pos = s.find(L'\n', pos_ant);
498 if (!s.empty())
499 AddEntry(s.substr(pos_ant, s.size() - 1), include);
502 // for all paths, have at least one entry in data
503 void ShellCache::CPathFilter::PostProcessData()
505 if (data.empty())
506 return;
508 std::sort(data.begin(), data.end());
510 // update subPathIncluded props and remove duplicate entries
511 auto begin = data.begin();
512 auto end = data.end();
513 auto dest = begin;
514 for (auto source = begin; source != end; ++source)
516 if (_wcsicmp(source->path.c_str(), dest->path.c_str()) == 0)
518 // multiple entries for the same path -> merge them
520 // update subPathIncluded
521 // (all relevant parent info has already been normalized)
522 if (!source->recursive)
523 source->subPathIncluded = IsPathAllowed(source->path.c_str(), begin, dest);
525 // multiple specs for the same path
526 // -> merge them into the existing entry @ dest
527 if (!source->recursive && dest->recursive)
529 // reset the marker for the this case
530 dest->recursive = false;
531 dest->included = source->included;
533 else
535 // include beats exclude
536 if (source->included == tristate_true)
537 dest->included = tristate_true;
538 if (source->recursive && source->subPathIncluded == tristate_true)
539 dest->subPathIncluded = tristate_true;
542 else
544 // new path -> don't merge this entry
545 size_t destSize = dest->path.size();
546 dest->hasSubFolderEntries = (source->path.size() > destSize) && (source->path[destSize] == L'\\') && (_wcsnicmp(source->path.substr(0, destSize).c_str(), dest->path.c_str(), destSize) == 0);
548 *++dest = *source;
550 // update subPathIncluded
551 // (all relevant parent info has already been normalized)
552 if (!dest->recursive)
553 dest->subPathIncluded = IsPathAllowed(source->path.c_str(), begin, dest);
557 // remove duplicate info
558 if (begin != end)
559 data.erase(++dest, end);
562 // lookup. default result is "unknown".
563 // We must look for *every* parent path because of situations like:
564 // excluded: C:, C:\some\deep\path
565 // include: C:\some
566 // lookup for C:\some\deeper\path
567 tristate_t ShellCache::CPathFilter::IsPathAllowed(LPCTSTR path, TData::const_iterator begin, TData::const_iterator end) const
569 tristate_t result = tristate_unknown;
571 // handle special cases
572 if (begin == end)
573 return result;
575 size_t maxLength = wcslen(path);
576 if (maxLength == 0)
577 return result;
579 // look for the most specific entry, start at the root
580 size_t pos = 0;
583 LPCTSTR backslash = wcschr(path + pos + 1,L'\\');
584 pos = backslash == nullptr ? maxLength : backslash - path;
586 std::pair<LPCTSTR, size_t> toFind(path, pos);
587 TData::const_iterator iter = std::lower_bound(begin, end, toFind);
589 // found a relevant entry?
590 if ((iter != end) && (iter->path.length() == pos) && (_wcsnicmp(iter->path.c_str(), path, pos) == 0))
592 // exact match?
593 if (pos == maxLength)
594 return iter->included;
596 // parent match
597 result = iter->subPathIncluded;
599 // done?
600 if (iter->hasSubFolderEntries)
601 begin = iter;
602 else
603 return result;
605 else
607 // set a (potentially) closer lower limit
608 if (iter != begin)
609 begin = --iter;
612 // set a (potentially) closer upper limit
613 end = std::upper_bound(begin, end, toFind);
614 } while ((pos < maxLength) && (begin != end));
616 // nothing more specific found
617 return result;
620 // construction
622 ShellCache::CPathFilter::CPathFilter()
623 : excludelist(L"Software\\TortoiseGit\\OverlayExcludeList", L"", false, HKEY_CURRENT_USER, KEY_WOW64_64KEY)
624 , includelist(L"Software\\TortoiseGit\\OverlayIncludeList", L"", false, HKEY_CURRENT_USER, KEY_WOW64_64KEY)
626 Refresh();
629 // notify of (potential) registry settings
631 void ShellCache::CPathFilter::Refresh()
633 excludelist.read();
634 includelist.read();
636 if ((excludeliststr.compare((tstring)excludelist) == 0) && (includeliststr.compare((tstring)includelist) == 0))
637 return;
639 excludeliststr = (tstring)excludelist;
640 includeliststr = (tstring)includelist;
641 data.clear();
642 AddEntries(excludeliststr, false);
643 AddEntries(includeliststr, true);
645 PostProcessData();
648 // data access
649 tristate_t ShellCache::CPathFilter::IsPathAllowed(LPCTSTR path) const
651 if (!path)
652 return tristate_unknown;
653 // always ignore the recycle bin
654 PTSTR pFound = StrStrI(path, L":\\RECYCLER");
655 if (pFound)
657 if ((*(pFound + 10) == L'\0') || (*(pFound + 10) == L'\\'))
658 return tristate_false;
660 pFound = StrStrI(path, L":\\$Recycle.Bin");
661 if (pFound)
663 if ((*(pFound + 14) == '\0') || (*(pFound + 14) == L'\\'))
664 return tristate_false;
666 return IsPathAllowed(path, data.begin(), data.end());