1///////////////////////////////////////////////////////////////////////////////
2// Name:        src/common/dircmn.cpp
3// Purpose:     wxDir methods common to all implementations
4// Author:      Vadim Zeitlin
5// Modified by:
6// Created:     19.05.01
7// RCS-ID:      $Id: dircmn.cpp 40665 2006-08-19 08:45:31Z JS $
8// Copyright:   (c) 2001 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9// License:     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/string.h"
29    #include "wx/log.h"
30    #include "wx/intl.h"
31    #include "wx/filefn.h"
32    #include "wx/arrstr.h"
33#endif //WX_PRECOMP
34
35#include "wx/dir.h"
36#include "wx/filename.h"
37
38// ============================================================================
39// implementation
40// ============================================================================
41
42// ----------------------------------------------------------------------------
43// wxDirTraverser
44// ----------------------------------------------------------------------------
45
46wxDirTraverseResult
47wxDirTraverser::OnOpenError(const wxString& WXUNUSED(dirname))
48{
49    return wxDIR_IGNORE;
50}
51
52// ----------------------------------------------------------------------------
53// wxDir::HasFiles() and HasSubDirs()
54// ----------------------------------------------------------------------------
55
56// dumb generic implementation
57
58bool wxDir::HasFiles(const wxString& spec)
59{
60    wxString s;
61    return GetFirst(&s, spec, wxDIR_FILES | wxDIR_HIDDEN);
62}
63
64// we have a (much) faster version for Unix
65#if (defined(__CYGWIN__) && defined(__WINDOWS__)) || !defined(__UNIX_LIKE__) || defined(__WXMAC__) || defined(__EMX__) || defined(__WINE__)
66
67bool wxDir::HasSubDirs(const wxString& spec)
68{
69    wxString s;
70    return GetFirst(&s, spec, wxDIR_DIRS | wxDIR_HIDDEN);
71}
72
73#endif // !Unix
74
75// ----------------------------------------------------------------------------
76// wxDir::Traverse()
77// ----------------------------------------------------------------------------
78
79size_t wxDir::Traverse(wxDirTraverser& sink,
80                       const wxString& filespec,
81                       int flags) const
82{
83    wxCHECK_MSG( IsOpened(), (size_t)-1,
84                 _T("dir must be opened before traversing it") );
85
86    // the total number of files found
87    size_t nFiles = 0;
88
89    // the name of this dir with path delimiter at the end
90    wxString prefix = GetName();
91    prefix += wxFILE_SEP_PATH;
92
93    // first, recurse into subdirs
94    if ( flags & wxDIR_DIRS )
95    {
96        wxString dirname;
97        for ( bool cont = GetFirst(&dirname, wxEmptyString, wxDIR_DIRS | (flags & wxDIR_HIDDEN) );
98              cont;
99              cont = cont && GetNext(&dirname) )
100        {
101            const wxString fulldirname = prefix + dirname;
102
103            switch ( sink.OnDir(fulldirname) )
104            {
105                default:
106                    wxFAIL_MSG(_T("unexpected OnDir() return value") );
107                    // fall through
108
109                case wxDIR_STOP:
110                    cont = false;
111                    break;
112
113                case wxDIR_CONTINUE:
114                    {
115                        wxDir subdir;
116
117                        // don't give the error messages for the directories
118                        // which we can't open: there can be all sorts of good
119                        // reason for this (e.g. insufficient privileges) and
120                        // this shouldn't be treated as an error -- instead
121                        // let the user code decide what to do
122                        bool ok;
123                        do
124                        {
125                            wxLogNull noLog;
126                            ok = subdir.Open(fulldirname);
127                            if ( !ok )
128                            {
129                                // ask the user code what to do
130                                bool tryagain;
131                                switch ( sink.OnOpenError(fulldirname) )
132                                {
133                                    default:
134                                        wxFAIL_MSG(_T("unexpected OnOpenError() return value") );
135                                        // fall through
136
137                                    case wxDIR_STOP:
138                                        cont = false;
139                                        // fall through
140
141                                    case wxDIR_IGNORE:
142                                        tryagain = false;
143                                        break;
144
145                                    case wxDIR_CONTINUE:
146                                        tryagain = true;
147                                }
148
149                                if ( !tryagain )
150                                    break;
151                            }
152                        }
153                        while ( !ok );
154
155                        if ( ok )
156                        {
157                            nFiles += subdir.Traverse(sink, filespec, flags);
158                        }
159                    }
160                    break;
161
162                case wxDIR_IGNORE:
163                    // nothing to do
164                    ;
165            }
166        }
167    }
168
169    // now enum our own files
170    if ( flags & wxDIR_FILES )
171    {
172        flags &= ~wxDIR_DIRS;
173
174        wxString filename;
175        bool cont = GetFirst(&filename, filespec, flags);
176        while ( cont )
177        {
178            wxDirTraverseResult res = sink.OnFile(prefix + filename);
179            if ( res == wxDIR_STOP )
180                break;
181
182            wxASSERT_MSG( res == wxDIR_CONTINUE,
183                          _T("unexpected OnFile() return value") );
184
185            nFiles++;
186
187            cont = GetNext(&filename);
188        }
189    }
190
191    return nFiles;
192}
193
194// ----------------------------------------------------------------------------
195// wxDir::GetAllFiles()
196// ----------------------------------------------------------------------------
197
198class wxDirTraverserSimple : public wxDirTraverser
199{
200public:
201    wxDirTraverserSimple(wxArrayString& files) : m_files(files) { }
202
203    virtual wxDirTraverseResult OnFile(const wxString& filename)
204    {
205        m_files.push_back(filename);
206        return wxDIR_CONTINUE;
207    }
208
209    virtual wxDirTraverseResult OnDir(const wxString& WXUNUSED(dirname))
210    {
211        return wxDIR_CONTINUE;
212    }
213
214private:
215    wxArrayString& m_files;
216
217    DECLARE_NO_COPY_CLASS(wxDirTraverserSimple)
218};
219
220/* static */
221size_t wxDir::GetAllFiles(const wxString& dirname,
222                          wxArrayString *files,
223                          const wxString& filespec,
224                          int flags)
225{
226    wxCHECK_MSG( files, (size_t)-1, _T("NULL pointer in wxDir::GetAllFiles") );
227
228    size_t nFiles = 0;
229
230    wxDir dir(dirname);
231    if ( dir.IsOpened() )
232    {
233        wxDirTraverserSimple traverser(*files);
234
235        nFiles += dir.Traverse(traverser, filespec, flags);
236    }
237
238    return nFiles;
239}
240
241// ----------------------------------------------------------------------------
242// wxDir::FindFirst()
243// ----------------------------------------------------------------------------
244
245class wxDirTraverserFindFirst : public wxDirTraverser
246{
247public:
248    wxDirTraverserFindFirst() { }
249
250    virtual wxDirTraverseResult OnFile(const wxString& filename)
251    {
252        m_file = filename;
253        return wxDIR_STOP;
254    }
255
256    virtual wxDirTraverseResult OnDir(const wxString& WXUNUSED(dirname))
257    {
258        return wxDIR_CONTINUE;
259    }
260
261    const wxString& GetFile() const
262    {
263        return m_file;
264    }
265
266private:
267    wxString m_file;
268
269    DECLARE_NO_COPY_CLASS(wxDirTraverserFindFirst)
270};
271
272/* static */
273wxString wxDir::FindFirst(const wxString& dirname,
274                          const wxString& filespec,
275                          int flags)
276{
277    wxDir dir(dirname);
278    if ( dir.IsOpened() )
279    {
280        wxDirTraverserFindFirst traverser;
281
282        dir.Traverse(traverser, filespec, flags | wxDIR_FILES);
283        return traverser.GetFile();
284    }
285
286    return wxEmptyString;
287}
288
289
290// ----------------------------------------------------------------------------
291// wxDir::GetTotalSize()
292// ----------------------------------------------------------------------------
293
294class wxDirTraverserSumSize : public wxDirTraverser
295{
296public:
297    wxDirTraverserSumSize() { }
298
299    virtual wxDirTraverseResult OnFile(const wxString& filename)
300    {
301        wxULongLong sz = wxFileName::GetSize(filename);
302
303        // wxFileName::GetSize won't use this class again as
304        // we're passing it a file and not a directory;
305        // thus we are sure to avoid an endless loop
306        if (sz == wxInvalidSize)
307        {
308            // if the GetSize() failed (this can happen because e.g. a
309            // file is locked by another process), we can proceed but
310            // we need to at least warn the user that the resulting
311            // final size could be not reliable (if e.g. the locked
312            // file is very big).
313            m_skippedFiles.Add(filename);
314            return wxDIR_CONTINUE;
315        }
316
317        m_sz += sz;
318        return wxDIR_CONTINUE;
319    }
320
321    virtual wxDirTraverseResult OnDir(const wxString& WXUNUSED(dirname))
322    {
323        return wxDIR_CONTINUE;
324    }
325
326    wxULongLong GetTotalSize() const
327        { return m_sz; }
328    wxArrayString &FilesSkipped()
329        { return m_skippedFiles; }
330
331protected:
332    wxULongLong m_sz;
333    wxArrayString m_skippedFiles;
334};
335
336wxULongLong wxDir::GetTotalSize(const wxString &dirname, wxArrayString *filesSkipped)
337{
338    if (!wxDirExists(dirname))
339        return wxInvalidSize;
340
341    // to get the size of this directory and its contents we need
342    // to recursively walk it...
343    wxDir dir(dirname);
344    if ( !dir.IsOpened() )
345        return wxInvalidSize;
346
347    wxDirTraverserSumSize traverser;
348    if (dir.Traverse(traverser) == (size_t)-1 ||
349        traverser.GetTotalSize() == 0)
350        return wxInvalidSize;
351
352    if (filesSkipped)
353        *filesSkipped = traverser.FilesSkipped();
354
355    return traverser.GetTotalSize();
356}
357
358