implemented wxDir::HasSubDirs() optimization for Unix
[wxWidgets.git] / src / unix / dir.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: unix/dir.cpp
3 // Purpose: wxDir implementation for Unix/POSIX systems
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 08.12.99
7 // RCS-ID: $Id$
8 // Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "dir.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #ifndef WX_PRECOMP
32 #include "wx/intl.h"
33 #include "wx/log.h"
34 #endif // PCH
35
36 #include "wx/dir.h"
37 #include "wx/filefn.h" // for wxMatchWild
38
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42
43 #include <dirent.h>
44
45 // ----------------------------------------------------------------------------
46 // macros
47 // ----------------------------------------------------------------------------
48
49 #define M_DIR ((wxDirData *)m_data)
50
51 // ----------------------------------------------------------------------------
52 // private classes
53 // ----------------------------------------------------------------------------
54
55 // this class stores everything we need to enumerate the files
56 class wxDirData
57 {
58 public:
59 wxDirData(const wxString& dirname);
60 ~wxDirData();
61
62 bool IsOk() const { return m_dir != NULL; }
63
64 void SetFileSpec(const wxString& filespec) { m_filespec = filespec; }
65 void SetFlags(int flags) { m_flags = flags; }
66
67 void Rewind() { rewinddir(m_dir); }
68 bool Read(wxString *filename);
69
70 const wxString& GetName() const { return m_dirname; }
71
72 private:
73 DIR *m_dir;
74
75 wxString m_dirname;
76 wxString m_filespec;
77
78 int m_flags;
79 };
80
81 // ============================================================================
82 // implementation
83 // ============================================================================
84
85 // ----------------------------------------------------------------------------
86 // wxDirData
87 // ----------------------------------------------------------------------------
88
89 #if !defined( __VMS__ ) || ( __VMS_VER >= 70000000 )
90
91 wxDirData::wxDirData(const wxString& dirname)
92 : m_dirname(dirname)
93 {
94 m_dir = NULL;
95
96 // throw away the trailing slashes
97 size_t n = m_dirname.length();
98 wxCHECK_RET( n, _T("empty dir name in wxDir") );
99
100 while ( n > 0 && m_dirname[--n] == '/' )
101 ;
102
103 m_dirname.Truncate(n + 1);
104
105 // do open the dir
106 m_dir = opendir(m_dirname.fn_str());
107 }
108
109 wxDirData::~wxDirData()
110 {
111 if ( m_dir )
112 {
113 if ( closedir(m_dir) != 0 )
114 {
115 wxLogLastError(_T("closedir"));
116 }
117 }
118 }
119
120 bool wxDirData::Read(wxString *filename)
121 {
122 dirent *de = (dirent *)NULL; // just to silence compiler warnings
123 bool matches = FALSE;
124
125 // speed up string concatenation in the loop a bit
126 wxString path = m_dirname;
127 path += _T('/');
128 path.reserve(path.length() + 255);
129
130 while ( !matches )
131 {
132 de = readdir(m_dir);
133 if ( !de )
134 return FALSE;
135
136 // don't return "." and ".." unless asked for
137 if ( de->d_name[0] == '.' &&
138 ((de->d_name[1] == '.' && de->d_name[2] == '\0') ||
139 (de->d_name[1] == '\0')) )
140 {
141 if ( !(m_flags & wxDIR_DOTDOT) )
142 continue;
143
144 // we found a valid match
145 break;
146 }
147
148 // check the type now
149 if ( !(m_flags & wxDIR_FILES) && !wxDir::Exists(path + de->d_name) )
150 {
151 // it's a file, but we don't want them
152 continue;
153 }
154 else if ( !(m_flags & wxDIR_DIRS) && wxDir::Exists(path + de->d_name) )
155 {
156 // it's a dir, and we don't want it
157 continue;
158 }
159
160 // finally, check the name
161 if ( m_filespec.empty() )
162 {
163 matches = m_flags & wxDIR_HIDDEN ? TRUE : de->d_name[0] != '.';
164 }
165 else
166 {
167 // test against the pattern
168 matches = wxMatchWild(m_filespec, de->d_name,
169 !(m_flags & wxDIR_HIDDEN));
170 }
171 }
172
173 *filename = de->d_name;
174
175 return TRUE;
176 }
177
178 #else // old VMS (TODO)
179
180 wxDirData::wxDirData(const wxString& WXUNUSED(dirname))
181 {
182 wxFAIL_MSG(_T("not implemented"));
183 }
184
185 wxDirData::~wxDirData()
186 {
187 }
188
189 bool wxDirData::Read(wxString * WXUNUSED(filename))
190 {
191 return FALSE;
192 }
193
194 #endif // not or new VMS/old VMS
195
196 // ----------------------------------------------------------------------------
197 // wxDir helpers
198 // ----------------------------------------------------------------------------
199
200 /* static */
201 bool wxDir::Exists(const wxString& dir)
202 {
203 return wxPathExists(dir);
204 }
205
206 // ----------------------------------------------------------------------------
207 // wxDir construction/destruction
208 // ----------------------------------------------------------------------------
209
210 wxDir::wxDir(const wxString& dirname)
211 {
212 m_data = NULL;
213
214 (void)Open(dirname);
215 }
216
217 bool wxDir::Open(const wxString& dirname)
218 {
219 delete M_DIR;
220 m_data = new wxDirData(dirname);
221
222 if ( !M_DIR->IsOk() )
223 {
224 wxLogSysError(_("Can not enumerate files in directory '%s'"),
225 dirname.c_str());
226
227 delete M_DIR;
228 m_data = NULL;
229
230 return FALSE;
231 }
232
233 return TRUE;
234 }
235
236 bool wxDir::IsOpened() const
237 {
238 return m_data != NULL;
239 }
240
241 wxString wxDir::GetName() const
242 {
243 wxString name;
244 if ( m_data )
245 {
246 name = M_DIR->GetName();
247 if ( !name.empty() && (name.Last() == _T('/')) )
248 {
249 // chop off the last (back)slash
250 name.Truncate(name.length() - 1);
251 }
252 }
253
254 return name;
255 }
256
257 wxDir::~wxDir()
258 {
259 delete M_DIR;
260 }
261
262 // ----------------------------------------------------------------------------
263 // wxDir enumerating
264 // ----------------------------------------------------------------------------
265
266 bool wxDir::GetFirst(wxString *filename,
267 const wxString& filespec,
268 int flags) const
269 {
270 wxCHECK_MSG( IsOpened(), FALSE, _T("must wxDir::Open() first") );
271
272 M_DIR->Rewind();
273
274 M_DIR->SetFileSpec(filespec);
275 M_DIR->SetFlags(flags);
276
277 return GetNext(filename);
278 }
279
280 bool wxDir::GetNext(wxString *filename) const
281 {
282 wxCHECK_MSG( IsOpened(), FALSE, _T("must wxDir::Open() first") );
283
284 wxCHECK_MSG( filename, FALSE, _T("bad pointer in wxDir::GetNext()") );
285
286 return M_DIR->Read(filename);
287 }
288
289 bool wxDir::HasSubDirs(const wxString& spec)
290 {
291 wxCHECK_MSG( IsOpened(), FALSE, _T("must wxDir::Open() first") );
292
293 if ( spec.empty() )
294 {
295 // faster check for presence of any subdirectory: normally each subdir
296 // has a hard link to the parent directory and so, knowing that there
297 // are at least "." and "..", we have a subdirectory if and only if
298 // links number is > 2 - this is just a guess but it works fairly well
299 // in practice
300 //
301 // note that we may guess wrongly in one direction only: i.e. we may
302 // return true when there are no subdirectories but this is ok as the
303 // caller will learn it soon enough when it calls GetFirst(wxDIR)
304 // anyhow
305 wxStructStat stBuf;
306 if ( wxStat(M_DIR->GetName(), &stBuf) == 0 )
307 {
308 switch ( stBuf.st_nlink )
309 {
310 case 2:
311 // just "." and ".."
312 return FALSE;
313
314 case 0:
315 case 1:
316 // weird filesystem, don't try to guess for it, use dumb
317 // method below
318 break;
319
320 default:
321 // assume we have subdirs - may turn out to be wrong if we
322 // have other hard links to this directory but it's not
323 // that bad as explained above
324 return TRUE;
325 }
326 }
327 }
328
329 // just try to find first directory
330 wxString s;
331 return GetFirst(&s, spec, wxDIR_DIRS | wxDIR_HIDDEN);
332 }
333