OSDN Git Service

3cc166d3da900c6e7dab285e4e9a42ed97d32fa4
[winmerge-jp/winmerge-jp.git] / Src / DirTravel.cpp
1 /** 
2  * @file  DirTravel.cpp
3  *
4  * @brief Implementation file for Directory traversal functions.
5  *
6  */
7
8 #include "DirTravel.h"
9 #include <algorithm>
10 #include <cstdint>
11 #include <Poco/DirectoryIterator.h>
12 #include <Poco/Timestamp.h>
13 #include <windows.h>
14 #include <tchar.h>
15 #include <mbstring.h>
16 #include "UnicodeString.h"
17 #include "DirItem.h"
18 #include "unicoder.h"
19 #include "paths.h"
20
21 using Poco::DirectoryIterator;
22 using Poco::Timestamp;
23
24 static void LoadFiles(const String& sDir, DirItemArray * dirs, DirItemArray * files);
25 static void Sort(DirItemArray * dirs, bool casesensitive);
26
27 /**
28  * @brief Load arrays with all directories & files in specified dir
29  */
30 void LoadAndSortFiles(const String& sDir, DirItemArray * dirs, DirItemArray * files, bool casesensitive)
31 {
32         LoadFiles(sDir, dirs, files);
33         Sort(dirs, casesensitive);
34         Sort(files, casesensitive);
35 }
36
37 /**
38  * @brief Find files and subfolders from given folder.
39  * This function saves all files and subfolders in given folder to arrays.
40  * We use 64-bit version of stat() to get times since find doesn't return
41  * valid times for very old files (around year 1970). Even stat() seems to
42  * give negative time values but we can live with that. Those around 1970
43  * times can happen when file is created so that it  doesn't get valid
44  * creation or modificatio dates.
45  * @param [in] sDir Base folder for files and subfolders.
46  * @param [in, out] dirs Array where subfolders are stored.
47  * @param [in, out] files Array where files are stored.
48  */
49 static void LoadFiles(const String& sDir, DirItemArray * dirs, DirItemArray * files)
50 {
51         boost::flyweight<String> dir(sDir);
52 #if 0
53         DirectoryIterator it(ucr::toUTF8(sDir));
54         DirectoryIterator end;
55
56         for (; it != end; ++it)
57         {
58                 bool bIsDirectory = it->isDirectory();
59                 if (bIsDirectory)
60                         continue;
61
62                 DirItem ent;
63                 ent.ctime = it->created();
64                 if (ent.ctime < 0)
65                         ent.ctime = 0;
66                 ent.mtime = it->getLastModified();
67                 if (ent.mtime < 0)
68                         ent.mtime = 0;
69                 ent.size = it->getSize();
70                 ent.path = dir;
71                 ent.filename = ucr::toTString(it.name());
72 #ifdef _WIN32
73                 ent.flags.attributes = GetFileAttributes(ucr::toTString(it.name()).c_str());;
74 #else
75 #endif
76                 
77                 (bIsDirectory ? dirs : files)->push_back(ent);
78         }
79
80 #else
81         String sPattern = paths_ConcatPath(sDir, _T("*.*"));
82
83         WIN32_FIND_DATA ff;
84         HANDLE h;
85         OSVERSIONINFO vi;
86         vi.dwOSVersionInfoSize = sizeof(vi);
87         GetVersionEx(&vi);
88         if (vi.dwMajorVersion * 10 + vi.dwMinorVersion >= 61)
89                 h = FindFirstFileEx(sPattern.c_str(), FindExInfoBasic, &ff, FindExSearchNameMatch, NULL, FIND_FIRST_EX_LARGE_FETCH);
90         else
91                 h = FindFirstFile(sPattern.c_str(), &ff);
92         if (h != INVALID_HANDLE_VALUE)
93         {
94                 do
95                 {
96                         bool bIsDirectory = (ff.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) > 0;
97                         if (bIsDirectory && _tcsstr(_T(".."), ff.cFileName))
98                                 continue;
99
100                         DirItem ent;
101
102                         // Save filetimes as seconds since January 1, 1970
103                         // Note that times can be < 0 if they are around that 1970..
104                         // Anyway that is not sensible case for normal files so we can
105                         // just use zero for their time.
106                         ent.ctime = Timestamp::fromFileTimeNP(ff.ftCreationTime.dwLowDateTime, ff.ftCreationTime.dwHighDateTime);
107                         if (ent.ctime < 0)
108                                 ent.ctime = 0;
109                         ent.mtime = Timestamp::fromFileTimeNP(ff.ftLastWriteTime.dwLowDateTime, ff.ftLastWriteTime.dwHighDateTime);
110                         if (ent.mtime < 0)
111                                 ent.mtime = 0;
112
113                         if (ff.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
114                                 ent.size = -1;  // No size for directories
115                         else
116                         {
117                                 ent.size = ((int64_t)ff.nFileSizeHigh << 32) + ff.nFileSizeLow;
118                         }
119
120                         ent.path = dir;
121                         ent.filename = ff.cFileName;
122                         ent.flags.attributes = ff.dwFileAttributes;
123                         
124                         (bIsDirectory ? dirs : files)->push_back(ent);
125                 } while (FindNextFile(h, &ff));
126                 FindClose(h);
127         }
128
129 #endif
130 }
131
132 static inline int collate(const String &str1, const String &str2)
133 {
134         return _tcscoll(str1.c_str(), str2.c_str());
135 }
136
137 static inline int collate_ignore_case(const String &str1, const String &str2)
138 {
139         return _tcsicoll(str1.c_str(), str2.c_str());
140 }
141
142
143 template<int (*compfunc)(const TCHAR *, const TCHAR *)>
144 struct StringComparer
145 {
146         bool operator()(const DirItem &elem1, const DirItem &elem2)
147         {
148                 return compfunc(elem1.filename.get().c_str(), elem2.filename.get().c_str()) < 0;
149         }
150 };
151
152 /**
153  * @brief sort specified array
154  */
155 static void Sort(DirItemArray * dirs, bool casesensitive)
156 {
157         if (casesensitive)
158         std::sort(dirs->begin(), dirs->end(), StringComparer<_tcscoll>());
159         else
160                 std::sort(dirs->begin(), dirs->end(), StringComparer<_tcsicoll>());
161 }
162
163 /**
164  * @brief  Compare (NLS aware) two strings, either case-sensitive or
165  * case-insensitive as caller specifies
166  */
167 int collstr(const String & s1, const String & s2, bool casesensitive)
168 {
169         if (casesensitive)
170                 return collate(s1, s2);
171         else
172                 return collate_ignore_case(s1, s2);
173 }