Added explicit Unicode conversion to wxDir - the
[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 wxString de_d_name;
131
132 while ( !matches )
133 {
134 de = readdir(m_dir);
135 if ( !de )
136 return FALSE;
137
138 #if wxUSE_UNICODE
139 de_d_name = wxConvLibc.cMB2WC( de->d_name );
140 #else
141 de_d_name = de->d_name;
142 #endif
143
144 // don't return "." and ".." unless asked for
145 if ( de->d_name[0] == '.' &&
146 ((de->d_name[1] == '.' && de->d_name[2] == '\0') ||
147 (de->d_name[1] == '\0')) )
148 {
149 if ( !(m_flags & wxDIR_DOTDOT) )
150 continue;
151
152 // we found a valid match
153 break;
154 }
155
156 // check the type now
157 if ( !(m_flags & wxDIR_FILES) && !wxDir::Exists(path + de_d_name) )
158 {
159 // it's a file, but we don't want them
160 continue;
161 }
162 else if ( !(m_flags & wxDIR_DIRS) && wxDir::Exists(path + de_d_name) )
163 {
164 // it's a dir, and we don't want it
165 continue;
166 }
167
168 // finally, check the name
169 if ( m_filespec.empty() )
170 {
171 matches = m_flags & wxDIR_HIDDEN ? TRUE : de->d_name[0] != '.';
172 }
173 else
174 {
175 #if wxUSE_UNICODE
176 matches = TRUE; // FIXME
177 #else
178 // test against the pattern
179 matches = wxMatchWild(m_filespec, de_d_name,
180 !(m_flags & wxDIR_HIDDEN));
181 #endif
182 }
183 }
184
185 *filename = de_d_name;
186
187 return TRUE;
188 }
189
190 #else // old VMS (TODO)
191
192 wxDirData::wxDirData(const wxString& WXUNUSED(dirname))
193 {
194 wxFAIL_MSG(_T("not implemented"));
195 }
196
197 wxDirData::~wxDirData()
198 {
199 }
200
201 bool wxDirData::Read(wxString * WXUNUSED(filename))
202 {
203 return FALSE;
204 }
205
206 #endif // not or new VMS/old VMS
207
208 // ----------------------------------------------------------------------------
209 // wxDir helpers
210 // ----------------------------------------------------------------------------
211
212 /* static */
213 bool wxDir::Exists(const wxString& dir)
214 {
215 return wxPathExists(dir);
216 }
217
218 // ----------------------------------------------------------------------------
219 // wxDir construction/destruction
220 // ----------------------------------------------------------------------------
221
222 wxDir::wxDir(const wxString& dirname)
223 {
224 m_data = NULL;
225
226 (void)Open(dirname);
227 }
228
229 bool wxDir::Open(const wxString& dirname)
230 {
231 delete M_DIR;
232 m_data = new wxDirData(dirname);
233
234 if ( !M_DIR->IsOk() )
235 {
236 wxLogSysError(_("Can not enumerate files in directory '%s'"),
237 dirname.c_str());
238
239 delete M_DIR;
240 m_data = NULL;
241
242 return FALSE;
243 }
244
245 return TRUE;
246 }
247
248 bool wxDir::IsOpened() const
249 {
250 return m_data != NULL;
251 }
252
253 wxString wxDir::GetName() const
254 {
255 wxString name;
256 if ( m_data )
257 {
258 name = M_DIR->GetName();
259 if ( !name.empty() && (name.Last() == _T('/')) )
260 {
261 // chop off the last (back)slash
262 name.Truncate(name.length() - 1);
263 }
264 }
265
266 return name;
267 }
268
269 wxDir::~wxDir()
270 {
271 delete M_DIR;
272 }
273
274 // ----------------------------------------------------------------------------
275 // wxDir enumerating
276 // ----------------------------------------------------------------------------
277
278 bool wxDir::GetFirst(wxString *filename,
279 const wxString& filespec,
280 int flags) const
281 {
282 wxCHECK_MSG( IsOpened(), FALSE, _T("must wxDir::Open() first") );
283
284 M_DIR->Rewind();
285
286 M_DIR->SetFileSpec(filespec);
287 M_DIR->SetFlags(flags);
288
289 return GetNext(filename);
290 }
291
292 bool wxDir::GetNext(wxString *filename) const
293 {
294 wxCHECK_MSG( IsOpened(), FALSE, _T("must wxDir::Open() first") );
295
296 wxCHECK_MSG( filename, FALSE, _T("bad pointer in wxDir::GetNext()") );
297
298 return M_DIR->Read(filename);
299 }
300
301 bool wxDir::HasSubDirs(const wxString& spec)
302 {
303 wxCHECK_MSG( IsOpened(), FALSE, _T("must wxDir::Open() first") );
304
305 if ( spec.empty() )
306 {
307 // faster check for presence of any subdirectory: normally each subdir
308 // has a hard link to the parent directory and so, knowing that there
309 // are at least "." and "..", we have a subdirectory if and only if
310 // links number is > 2 - this is just a guess but it works fairly well
311 // in practice
312 //
313 // note that we may guess wrongly in one direction only: i.e. we may
314 // return true when there are no subdirectories but this is ok as the
315 // caller will learn it soon enough when it calls GetFirst(wxDIR)
316 // anyhow
317 wxStructStat stBuf;
318 if ( wxStat(M_DIR->GetName().c_str(), &stBuf) == 0 )
319 {
320 switch ( stBuf.st_nlink )
321 {
322 case 2:
323 // just "." and ".."
324 return FALSE;
325
326 case 0:
327 case 1:
328 // weird filesystem, don't try to guess for it, use dumb
329 // method below
330 break;
331
332 default:
333 // assume we have subdirs - may turn out to be wrong if we
334 // have other hard links to this directory but it's not
335 // that bad as explained above
336 return TRUE;
337 }
338 }
339 }
340
341 // just try to find first directory
342 wxString s;
343 return GetFirst(&s, spec, wxDIR_DIRS | wxDIR_HIDDEN);
344 }
345