OSDN Git Service

2db1f0e8383cd2b1dcdecf3c43d0fad5e06c6c13
[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 // ID line follows -- this is updated by SVN
8 // $Id: DirTravel.cpp 5761 2008-08-08 04:54:52Z marcelgosselin $
9
10 #include "DirTravel.h"
11 #include <algorithm>
12 #include <Poco/DirectoryIterator.h>
13 #include <Poco/Timestamp.h>
14 #include <windows.h>
15 #include <tchar.h>
16 #include <mbstring.h>
17 #include "UnicodeString.h"
18 #include "DirItem.h"
19 #include "unicoder.h"
20
21 using Poco::DirectoryIterator;
22 using Poco::Timestamp;
23 using Poco::Int64;
24 using boost::shared_ptr;
25
26 static void LoadFiles(const String& sDir, DirItemArray * dirs, DirItemArray * files);
27 static void Sort(DirItemArray * dirs, bool casesensitive);
28
29 /**
30  * @brief Load arrays with all directories & files in specified dir
31  */
32 void LoadAndSortFiles(const String& sDir, DirItemArray * dirs, DirItemArray * files, bool casesensitive)
33 {
34         LoadFiles(sDir, dirs, files);
35         Sort(dirs, casesensitive);
36         Sort(files, casesensitive);
37 }
38
39 /**
40  * @brief Find files and subfolders from given folder.
41  * This function saves all files and subfolders in given folder to arrays.
42  * We use 64-bit version of stat() to get times since find doesn't return
43  * valid times for very old files (around year 1970). Even stat() seems to
44  * give negative time values but we can live with that. Those around 1970
45  * times can happen when file is created so that it  doesn't get valid
46  * creation or modificatio dates.
47  * @param [in] sDir Base folder for files and subfolders.
48  * @param [in, out] dirs Array where subfolders are stored.
49  * @param [in, out] files Array where files are stored.
50  */
51 static void LoadFiles(const String& sDir, DirItemArray * dirs, DirItemArray * files)
52 {
53         boost::flyweight<String> dir(sDir);
54 #if 0
55         DirectoryIterator it(ucr::toUTF8(sDir));
56         DirectoryIterator end;
57
58         for (; it != end; ++it)
59         {
60                 bool bIsDirectory = it->isDirectory();
61                 if (bIsDirectory)
62                         continue;
63
64                 DirItem ent;
65                 ent.ctime = it->created();
66                 if (ent.ctime < 0)
67                         ent.ctime = 0;
68                 ent.mtime = it->getLastModified();
69                 if (ent.mtime < 0)
70                         ent.mtime = 0;
71                 ent.size = it->getSize();
72                 ent.path = dir;
73                 ent.filename = ucr::toTString(it.name());
74 #ifdef _WIN32
75                 ent.flags.attributes = GetFileAttributes(ucr::toTString(it.name()).c_str());;
76 #else
77 #endif
78                 
79                 (bIsDirectory ? dirs : files)->push_back(ent);
80         }
81
82 #else
83         String sPattern(sDir);
84         size_t len = sPattern.length();
85         if (sPattern[len - 1] != '\\')
86                 sPattern += _T("\\*.*");
87         else
88         sPattern += _T("*.*");
89
90         WIN32_FIND_DATA ff;
91         HANDLE 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)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 int collate(const String &str1, const String &str2)
133 {
134         return _tcscoll(str1.c_str(), str2.c_str());
135 }
136
137 /**
138  * @brief case-sensitive collate function for qsorting an array
139  */
140 static bool __cdecl cmpstring(const DirItem &elem1, const DirItem &elem2)
141 {
142         return collate(elem1.GetFileName(), elem2.GetFileName()) < 0;
143 }
144
145 static int collate_ignore_case(const String &str1, const String &str2)
146 {
147         String s1(str1);
148         String s2(str2);
149     String::size_type i = 0;
150 #ifdef _UNICODE
151         for (i = 0; i < s1.length(); i++)
152                 s1[i] = _totlower(s1[i]);
153         for (i = 0; i < s2.length(); i++)
154                 s2[i] = _totlower(s2[i]);
155 #else
156         for (i = 0; i < s1.length(); i++)
157         {
158                 if (_ismbblead(s1[i]))
159                         i++;
160                 else
161                         s1[i] = _totlower(s1[i]);
162         }
163         for (i = 0; i < s2.length(); i++)
164         {
165                 if (_ismbblead(s2[i]))
166                         i++;
167                 else
168                         s2[i] = _totlower(s2[i]);
169         }
170 #endif
171         return _tcscoll(s1.c_str(), s2.c_str());
172 }
173
174 /**
175  * @brief case-insensitive collate function for qsorting an array
176  */
177 static bool __cdecl cmpistring(const DirItem &elem1, const DirItem &elem2)
178 {
179         return collate_ignore_case(elem1.GetFileName(), elem2.GetFileName()) < 0;
180 }
181
182 /**
183  * @brief sort specified array
184  */
185 static void Sort(DirItemArray * dirs, bool casesensitive)
186 {
187         if (casesensitive)
188         std::sort(dirs->begin(), dirs->end(), cmpstring);
189         else
190                 std::sort(dirs->begin(), dirs->end(), cmpistring);
191 }
192
193 /**
194  * @brief  Compare (NLS aware) two strings, either case-sensitive or
195  * case-insensitive as caller specifies
196  */
197 int collstr(const String & s1, const String & s2, bool casesensitive)
198 {
199         if (casesensitive)
200                 return collate(s1, s2);
201         else
202                 return collate_ignore_case(s1, s2);
203 }