OSDN Git Service

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