1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2015-2017 - TortoiseGit
4 // Copyright (C) 2005-2006, 2009-2010 - 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 "DirFileEnum.h"
22 #include "StringUtils.h"
24 CSimpleFileFind::CSimpleFileFind(const CString
&sPath
, LPCTSTR pPattern
)
25 : m_dError(ERROR_SUCCESS
)
27 , m_sPathPrefix(sPath
)
29 // Add a trailing \ to m_sPathPrefix if it is missing.
30 // Do not add one to "C:" since "C:" and "C:\" are different.
31 int len
= m_sPathPrefix
.GetLength();
34 TCHAR ch
= sPath
[len
-1];
35 if (ch
!= '\\' && (ch
!= ':' || len
!= 2))
36 m_sPathPrefix
+= "\\";
38 if (len
>= 248 && (CStringUtils::StartsWith(m_sPathPrefix
, L
"\\\\?\\")))
39 m_hFindFile
= ::FindFirstFileEx((LPCTSTR
)(L
"\\\\?\\" + m_sPathPrefix
+ pPattern
), FindExInfoBasic
, &m_FindFileData
, FindExSearchNameMatch
, nullptr, FIND_FIRST_EX_LARGE_FETCH
);
41 m_hFindFile
= ::FindFirstFileEx((LPCTSTR
)(m_sPathPrefix
+ pPattern
), FindExInfoBasic
, &m_FindFileData
, FindExSearchNameMatch
, nullptr, FIND_FIRST_EX_LARGE_FETCH
);
42 if (m_hFindFile
== INVALID_HANDLE_VALUE
) {
43 m_dError
= ::GetLastError();
47 CSimpleFileFind::~CSimpleFileFind()
49 if (m_hFindFile
!= INVALID_HANDLE_VALUE
)
50 ::FindClose(m_hFindFile
);
53 BOOL
CSimpleFileFind::FindNextFile()
64 if (!::FindNextFile(m_hFindFile
, &m_FindFileData
))
66 m_dError
= ::GetLastError();
73 BOOL
CSimpleFileFind::FindNextFileNoDots()
77 result
= FindNextFile();
78 } while (result
&& IsDots());
83 BOOL
CSimpleFileFind::FindNextFileNoDirectories()
87 result
= FindNextFile();
88 } while (result
&& IsDirectory());
95 * Implementation notes:
97 * This is a depth-first traversal. Directories are visited before
100 * We keep a stack of directories. The deepest directory is at the top
101 * of the stack, the originally-requested directory is at the bottom.
102 * If we come across a directory, we first return it to the user, then
103 * recurse into it. The finder at the bottom of the stack always points
104 * to the file or directory last returned to the user (except immediately
105 * after creation, when the finder points to the first valid thing we need
106 * to return, but we haven't actually returned anything yet - hence the
107 * m_bIsNew variable).
109 * Errors reading a directory are assumed to be end-of-directory, and
110 * are otherwise ignored.
112 * The "." and ".." psedo-directories are ignored for obvious reasons.
116 CDirFileEnum::CDirStackEntry::CDirStackEntry(CDirStackEntry
* seNext
, const CString
& sDirName
)
117 : CSimpleFileFind(sDirName
)
122 CDirFileEnum::CDirStackEntry::~CDirStackEntry()
126 inline void CDirFileEnum::PopStack()
128 CDirStackEntry
* seToDelete
= m_seStack
;
129 m_seStack
= seToDelete
->m_seNext
;
133 inline void CDirFileEnum::PushStack(const CString
& sDirName
)
135 m_seStack
= new CDirStackEntry(m_seStack
,sDirName
);
138 CDirFileEnum::CDirFileEnum(const CString
& sDirName
)
145 CDirFileEnum::~CDirFileEnum()
151 BOOL
CDirFileEnum::NextFile(CString
&sResult
, bool* pbIsDirectory
, bool bRecurse
/* = true */)
155 // Special-case first time - haven't found anything yet,
156 // so don't do recurse-into-directory check.
159 else if (m_seStack
&& m_seStack
->IsDirectory() && bRecurse
)
160 PushStack(m_seStack
->GetFilePath());
162 while (m_seStack
&& !m_seStack
->FindNextFileNoDots())
164 // No more files in this directory, try parent.
170 sResult
= m_seStack
->GetFilePath();
172 *pbIsDirectory
= m_seStack
->IsDirectory();