OSDN Git Service

Merge branch 'master' of git://repo.or.cz/TortoiseGit
[tortoisegit/TortoiseGitJp.git] / src / Utils / DirFileEnum.cpp
1 // TortoiseSVN - a Windows shell extension for easy version control\r
2 \r
3 // Copyright (C) 2005 - 2006 - Jon Foster\r
4 \r
5 // This program is free software; you can redistribute it and/or\r
6 // modify it under the terms of the GNU General Public License\r
7 // as published by the Free Software Foundation; either version 2\r
8 // of the License, or (at your option) any later version.\r
9 \r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 \r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, write to the Free Software Foundation,\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
18 #include "StdAfx.h"\r
19 #include "DirFileEnum.h"\r
20 \r
21 \r
22 CSimpleFileFind::CSimpleFileFind(const CString &sPath, LPCTSTR pPattern) :\r
23    m_dError(ERROR_SUCCESS),\r
24    m_bFirst(TRUE),\r
25    m_sPathPrefix(sPath)\r
26 {\r
27    // Add a trailing \ to m_sPathPrefix if it is missing.\r
28    // Do not add one to "C:" since "C:" and "C:\" are different.\r
29    {\r
30       int len = m_sPathPrefix.GetLength();\r
31       if (len != 0) {\r
32          TCHAR ch = sPath[len-1];\r
33          if (ch != '\\' && (ch != ':' || len != 2)) {\r
34             m_sPathPrefix += "\\";\r
35          }\r
36       }\r
37    }\r
38 \r
39    m_hFindFile = ::FindFirstFile((LPCTSTR)(m_sPathPrefix + pPattern), &m_FindFileData); \r
40    if (m_hFindFile == INVALID_HANDLE_VALUE) {\r
41       m_dError = ::GetLastError();\r
42    }\r
43 }\r
44 \r
45 CSimpleFileFind::~CSimpleFileFind()\r
46 {\r
47    if (m_hFindFile != INVALID_HANDLE_VALUE) {\r
48       ::FindClose(m_hFindFile);\r
49    }\r
50 }\r
51 \r
52 BOOL CSimpleFileFind::FindNextFile()\r
53 {\r
54    if (m_dError) {\r
55       return FALSE;\r
56    }\r
57 \r
58    if (m_bFirst) {\r
59       m_bFirst = FALSE;\r
60       return TRUE;\r
61    }\r
62 \r
63    if (!::FindNextFile(m_hFindFile, &m_FindFileData)) {\r
64       m_dError = ::GetLastError();\r
65       return FALSE;\r
66    }\r
67 \r
68    return TRUE;\r
69 }\r
70 \r
71 BOOL CSimpleFileFind::FindNextFileNoDots()\r
72 {\r
73    BOOL result;\r
74    do {\r
75       result = FindNextFile();\r
76    } while (result && IsDots());\r
77 \r
78    return result;\r
79 }\r
80 \r
81 BOOL CSimpleFileFind::FindNextFileNoDirectories()\r
82 {\r
83    BOOL result;\r
84    do {\r
85       result = FindNextFile();\r
86    } while (result && IsDirectory());\r
87 \r
88    return result;\r
89 }\r
90 \r
91 \r
92 /*\r
93  * Implementation notes:\r
94  *\r
95  * This is a depth-first traversal.  Directories are visited before\r
96  * their contents.\r
97  *\r
98  * We keep a stack of directories.  The deepest directory is at the top\r
99  * of the stack, the originally-requested directory is at the bottom.\r
100  * If we come across a directory, we first return it to the user, then\r
101  * recurse into it.  The finder at the bottom of the stack always points\r
102  * to the file or directory last returned to the user (except immediately\r
103  * after creation, when the finder points to the first valid thing we need\r
104  * to return, but we haven't actually returned anything yet - hence the\r
105  * m_bIsNew variable).\r
106  *\r
107  * Errors reading a directory are assumed to be end-of-directory, and\r
108  * are otherwise ignored.\r
109  *\r
110  * The "." and ".." psedo-directories are ignored for obvious reasons.\r
111  */\r
112 \r
113 \r
114 CDirFileEnum::CDirStackEntry::CDirStackEntry(CDirStackEntry * seNext,\r
115                                              const CString& sDirName)\r
116                                              : CSimpleFileFind(sDirName),\r
117                                              m_seNext(seNext)\r
118 {\r
119 }\r
120 \r
121 CDirFileEnum::CDirStackEntry::~CDirStackEntry()\r
122 {\r
123 }\r
124 \r
125 inline void CDirFileEnum::PopStack()\r
126 {\r
127    CDirStackEntry * seToDelete = m_seStack;\r
128    m_seStack = seToDelete->m_seNext;\r
129    delete seToDelete;\r
130 }\r
131 \r
132 inline void CDirFileEnum::PushStack(const CString& sDirName)\r
133 {\r
134    m_seStack = new CDirStackEntry(m_seStack,sDirName);\r
135 }\r
136 \r
137 CDirFileEnum::CDirFileEnum(const CString& sDirName) :\r
138    m_seStack(NULL),\r
139    m_bIsNew(TRUE)\r
140 {\r
141    PushStack(sDirName);\r
142 }\r
143 \r
144 CDirFileEnum::~CDirFileEnum()\r
145 {\r
146    while (m_seStack != NULL) {\r
147       PopStack();\r
148    }\r
149 }\r
150 \r
151 BOOL CDirFileEnum::NextFile(CString &sResult, bool* pbIsDirectory)\r
152 {\r
153    if (m_bIsNew) {\r
154       // Special-case first time - haven't found anything yet,\r
155       // so don't do recurse-into-directory check.\r
156       m_bIsNew = FALSE;\r
157    } else if (m_seStack && m_seStack->IsDirectory()) {\r
158       PushStack(m_seStack->GetFilePath());\r
159    }\r
160 \r
161    while (m_seStack && !m_seStack->FindNextFileNoDots()) {\r
162       // No more files in this directory, try parent.\r
163       PopStack();\r
164    }\r
165 \r
166    if (m_seStack) \r
167    {\r
168       sResult = m_seStack->GetFilePath();\r
169           if(pbIsDirectory != NULL)\r
170           {\r
171                   *pbIsDirectory = m_seStack->IsDirectory();\r
172           }\r
173       return TRUE;\r
174    } else {\r
175       return FALSE;\r
176    }\r
177 }\r