1/////////////////////////////////////////////////////////////////////////////
2// Name:        msw/dir.cpp
3// Purpose:     wxDir implementation for Win32
4// Author:      Vadim Zeitlin
5// Modified by:
6// Created:     08.12.99
7// RCS-ID:      $Id: dir.cpp 42910 2006-11-01 15:29:58Z JS $
8// Copyright:   (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9// Licence:     wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
24    #pragma hdrstop
25#endif
26
27#ifndef WX_PRECOMP
28    #include "wx/intl.h"
29    #include "wx/log.h"
30#endif // PCH
31
32#include "wx/dir.h"
33#include "wx/filefn.h"          // for wxDirExists()
34
35#ifdef __WINDOWS__
36    #include "wx/msw/private.h"
37#endif
38
39// ----------------------------------------------------------------------------
40// define the types and functions used for file searching
41// ----------------------------------------------------------------------------
42
43typedef WIN32_FIND_DATA FIND_STRUCT;
44typedef HANDLE FIND_DATA;
45typedef DWORD FIND_ATTR;
46
47static inline FIND_DATA InitFindData() { return INVALID_HANDLE_VALUE; }
48
49static inline bool IsFindDataOk(FIND_DATA fd)
50{
51        return fd != INVALID_HANDLE_VALUE;
52}
53
54static inline void FreeFindData(FIND_DATA fd)
55{
56        if ( !::FindClose(fd) )
57        {
58            wxLogLastError(_T("FindClose"));
59        }
60}
61
62static inline FIND_DATA FindFirst(const wxString& spec,
63                                      FIND_STRUCT *finddata)
64{
65        return ::FindFirstFile(spec, finddata);
66}
67
68static inline bool FindNext(FIND_DATA fd, FIND_STRUCT *finddata)
69{
70        return ::FindNextFile(fd, finddata) != 0;
71}
72
73static const wxChar *GetNameFromFindData(FIND_STRUCT *finddata)
74{
75        return finddata->cFileName;
76}
77
78static const FIND_ATTR GetAttrFromFindData(FIND_STRUCT *finddata)
79{
80        return finddata->dwFileAttributes;
81}
82
83static inline bool IsDir(FIND_ATTR attr)
84{
85        return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0;
86}
87
88static inline bool IsHidden(FIND_ATTR attr)
89{
90        return (attr & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0;
91}
92
93// ----------------------------------------------------------------------------
94// constants
95// ----------------------------------------------------------------------------
96
97#ifndef MAX_PATH
98    #define MAX_PATH 260        // from VC++ headers
99#endif
100
101// ----------------------------------------------------------------------------
102// macros
103// ----------------------------------------------------------------------------
104
105#define M_DIR       ((wxDirData *)m_data)
106
107// ----------------------------------------------------------------------------
108// private classes
109// ----------------------------------------------------------------------------
110
111// this class stores everything we need to enumerate the files
112class wxDirData
113{
114public:
115    wxDirData(const wxString& dirname);
116    ~wxDirData();
117
118    void SetFileSpec(const wxString& filespec) { m_filespec = filespec; }
119    void SetFlags(int flags) { m_flags = flags; }
120
121    void Close();
122    void Rewind();
123    bool Read(wxString *filename);
124
125    const wxString& GetName() const { return m_dirname; }
126
127private:
128    FIND_DATA m_finddata;
129
130    wxString m_dirname;
131    wxString m_filespec;
132
133    int      m_flags;
134
135    DECLARE_NO_COPY_CLASS(wxDirData)
136};
137
138// ============================================================================
139// implementation
140// ============================================================================
141
142// ----------------------------------------------------------------------------
143// wxDirData
144// ----------------------------------------------------------------------------
145
146wxDirData::wxDirData(const wxString& dirname)
147         : m_dirname(dirname)
148{
149    m_finddata = InitFindData();
150}
151
152wxDirData::~wxDirData()
153{
154    Close();
155}
156
157void wxDirData::Close()
158{
159    if ( IsFindDataOk(m_finddata) )
160    {
161        FreeFindData(m_finddata);
162
163        m_finddata = InitFindData();
164    }
165}
166
167void wxDirData::Rewind()
168{
169    Close();
170}
171
172bool wxDirData::Read(wxString *filename)
173{
174    bool first = false;
175
176    WIN32_FIND_DATA finddata;
177    #define PTR_TO_FINDDATA (&finddata)
178
179    if ( !IsFindDataOk(m_finddata) )
180    {
181        // open first
182        wxString filespec = m_dirname;
183        if ( !wxEndsWithPathSeparator(filespec) )
184        {
185            filespec += _T('\\');
186        }
187        filespec += (!m_filespec ? _T("*.*") : m_filespec.c_str());
188
189        m_finddata = FindFirst(filespec, PTR_TO_FINDDATA);
190
191        first = true;
192    }
193
194    if ( !IsFindDataOk(m_finddata) )
195    {
196#ifdef __WIN32__
197        DWORD err = ::GetLastError();
198
199        if ( err != ERROR_FILE_NOT_FOUND && err != ERROR_NO_MORE_FILES )
200        {
201            wxLogSysError(err, _("Can not enumerate files in directory '%s'"),
202                          m_dirname.c_str());
203        }
204#endif // __WIN32__
205        //else: not an error, just no (such) files
206
207        return false;
208    }
209
210    const wxChar *name;
211    FIND_ATTR attr;
212
213    for ( ;; )
214    {
215        if ( first )
216        {
217            first = false;
218        }
219        else
220        {
221            if ( !FindNext(m_finddata, PTR_TO_FINDDATA) )
222            {
223#ifdef __WIN32__
224                DWORD err = ::GetLastError();
225
226                if ( err != ERROR_NO_MORE_FILES )
227                {
228                    wxLogLastError(_T("FindNext"));
229                }
230#endif // __WIN32__
231                //else: not an error, just no more (such) files
232
233                return false;
234            }
235        }
236
237        name = GetNameFromFindData(PTR_TO_FINDDATA);
238        attr = GetAttrFromFindData(PTR_TO_FINDDATA);
239
240        // don't return "." and ".." unless asked for
241        if ( name[0] == _T('.') &&
242             ((name[1] == _T('.') && name[2] == _T('\0')) ||
243              (name[1] == _T('\0'))) )
244        {
245            if ( !(m_flags & wxDIR_DOTDOT) )
246                continue;
247        }
248
249        // check the type now
250        if ( !(m_flags & wxDIR_FILES) && !IsDir(attr) )
251        {
252            // it's a file, but we don't want them
253            continue;
254        }
255        else if ( !(m_flags & wxDIR_DIRS) && IsDir(attr) )
256        {
257            // it's a dir, and we don't want it
258            continue;
259        }
260
261        // finally, check whether it's a hidden file
262        if ( !(m_flags & wxDIR_HIDDEN) )
263        {
264            if ( IsHidden(attr) )
265            {
266                // it's a hidden file, skip it
267                continue;
268            }
269        }
270
271        *filename = name;
272
273        break;
274    }
275
276    return true;
277}
278
279// ----------------------------------------------------------------------------
280// wxDir helpers
281// ----------------------------------------------------------------------------
282
283/* static */
284bool wxDir::Exists(const wxString& dir)
285{
286    return wxDirExists(dir);
287}
288
289// ----------------------------------------------------------------------------
290// wxDir construction/destruction
291// ----------------------------------------------------------------------------
292
293wxDir::wxDir(const wxString& dirname)
294{
295    m_data = NULL;
296
297    (void)Open(dirname);
298}
299
300bool wxDir::Open(const wxString& dirname)
301{
302    delete M_DIR;
303    m_data = new wxDirData(dirname);
304
305    return true;
306}
307
308bool wxDir::IsOpened() const
309{
310    return m_data != NULL;
311}
312
313wxString wxDir::GetName() const
314{
315    wxString name;
316    if ( m_data )
317    {
318        name = M_DIR->GetName();
319        if ( !name.empty() )
320        {
321            // bring to canonical Windows form
322            name.Replace(_T("/"), _T("\\"));
323
324            if ( name.Last() == _T('\\') )
325            {
326                // chop off the last (back)slash
327                name.Truncate(name.length() - 1);
328            }
329        }
330    }
331
332    return name;
333}
334
335wxDir::~wxDir()
336{
337    delete M_DIR;
338}
339
340// ----------------------------------------------------------------------------
341// wxDir enumerating
342// ----------------------------------------------------------------------------
343
344bool wxDir::GetFirst(wxString *filename,
345                     const wxString& filespec,
346                     int flags) const
347{
348    wxCHECK_MSG( IsOpened(), false, _T("must wxDir::Open() first") );
349
350    M_DIR->Rewind();
351
352    M_DIR->SetFileSpec(filespec);
353    M_DIR->SetFlags(flags);
354
355    return GetNext(filename);
356}
357
358bool wxDir::GetNext(wxString *filename) const
359{
360    wxCHECK_MSG( IsOpened(), false, _T("must wxDir::Open() first") );
361
362    wxCHECK_MSG( filename, false, _T("bad pointer in wxDir::GetNext()") );
363
364    return M_DIR->Read(filename);
365}
366
367// ----------------------------------------------------------------------------
368// wxGetDirectoryTimes: used by wxFileName::GetTimes()
369// ----------------------------------------------------------------------------
370
371#ifdef __WIN32__
372
373extern bool
374wxGetDirectoryTimes(const wxString& dirname,
375                    FILETIME *ftAccess, FILETIME *ftCreate, FILETIME *ftMod)
376{
377#ifdef __WXWINCE__
378    // FindFirst() is going to fail
379    wxASSERT_MSG( !dirname.empty(),
380                  _T("incorrect directory name format in wxGetDirectoryTimes") );
381#else
382    // FindFirst() is going to fail
383    wxASSERT_MSG( !dirname.empty() && dirname.Last() != _T('\\'),
384                  _T("incorrect directory name format in wxGetDirectoryTimes") );
385#endif
386
387    FIND_STRUCT fs;
388    FIND_DATA fd = FindFirst(dirname, &fs);
389    if ( !IsFindDataOk(fd) )
390    {
391        return false;
392    }
393
394    *ftAccess = fs.ftLastAccessTime;
395    *ftCreate = fs.ftCreationTime;
396    *ftMod = fs.ftLastWriteTime;
397
398    FindClose(fd);
399
400    return true;
401}
402
403#endif // __WIN32__
404
405