1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/common/filesys.cpp
3// Purpose:     wxFileSystem class - interface for opening files
4// Author:      Vaclav Slavik
5// Copyright:   (c) 1999 Vaclav Slavik
6// CVS-ID:      $Id: filesys.cpp 55271 2008-08-26 00:03:04Z VZ $
7// Licence:     wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10#include "wx/wxprec.h"
11
12#ifdef __BORLANDC__
13    #pragma hdrstop
14#endif
15
16
17#if wxUSE_FILESYSTEM
18
19#include "wx/filesys.h"
20
21#ifndef WX_PRECOMP
22    #include "wx/log.h"
23    #include "wx/module.h"
24#endif
25
26#include "wx/sysopt.h"
27#include "wx/wfstream.h"
28#include "wx/mimetype.h"
29#include "wx/filename.h"
30#include "wx/tokenzr.h"
31#include "wx/uri.h"
32#include "wx/private/fileback.h"
33
34
35//--------------------------------------------------------------------------------
36// wxFileSystemHandler
37//--------------------------------------------------------------------------------
38
39IMPLEMENT_ABSTRACT_CLASS(wxFileSystemHandler, wxObject)
40
41
42wxString wxFileSystemHandler::GetMimeTypeFromExt(const wxString& location)
43{
44    wxString ext, mime;
45    wxString loc = GetRightLocation(location);
46    wxChar c;
47    int l = loc.length(), l2;
48
49    l2 = l;
50    for (int i = l-1; i >= 0; i--)
51    {
52        c = loc[(unsigned int) i];
53        if ( c == wxT('#') )
54            l2 = i + 1;
55        if ( c == wxT('.') )
56        {
57            ext = loc.Right(l2-i-1);
58            break;
59        }
60        if ( (c == wxT('/')) || (c == wxT('\\')) || (c == wxT(':')) )
61            return wxEmptyString;
62    }
63
64#if wxUSE_MIMETYPE
65    static bool s_MinimalMimeEnsured = false;
66
67    // Don't use mime types manager if the application doesn't need it and it would be
68    // cause an unacceptable delay, especially on startup.
69    bool useMimeTypesManager = true;
70#if wxUSE_SYSTEM_OPTIONS
71    useMimeTypesManager = (wxSystemOptions::GetOptionInt(wxT("filesys.no-mimetypesmanager")) == 0);
72#endif
73
74    if (useMimeTypesManager)
75    {
76        if (!s_MinimalMimeEnsured)
77        {
78            static const wxFileTypeInfo fallbacks[] =
79            {
80                wxFileTypeInfo(_T("image/jpeg"),
81                    wxEmptyString,
82                    wxEmptyString,
83                    _T("JPEG image (from fallback)"),
84                    _T("jpg"), _T("jpeg"), _T("JPG"), _T("JPEG"), NULL),
85                    wxFileTypeInfo(_T("image/gif"),
86                    wxEmptyString,
87                    wxEmptyString,
88                    _T("GIF image (from fallback)"),
89                    _T("gif"), _T("GIF"), NULL),
90                    wxFileTypeInfo(_T("image/png"),
91                    wxEmptyString,
92                    wxEmptyString,
93                    _T("PNG image (from fallback)"),
94                    _T("png"), _T("PNG"), NULL),
95                    wxFileTypeInfo(_T("image/bmp"),
96                    wxEmptyString,
97                    wxEmptyString,
98                    _T("windows bitmap image (from fallback)"),
99                    _T("bmp"), _T("BMP"), NULL),
100                    wxFileTypeInfo(_T("text/html"),
101                    wxEmptyString,
102                    wxEmptyString,
103                    _T("HTML document (from fallback)"),
104                    _T("htm"), _T("html"), _T("HTM"), _T("HTML"), NULL),
105                    // must terminate the table with this!
106                    wxFileTypeInfo()
107            };
108            wxTheMimeTypesManager->AddFallbacks(fallbacks);
109            s_MinimalMimeEnsured = true;
110        }
111
112        wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(ext);
113        if ( !ft || !ft -> GetMimeType(&mime) )
114        {
115            mime = wxEmptyString;
116        }
117
118        delete ft;
119
120        return mime;
121    }
122    else
123#endif
124    {
125        if ( ext.IsSameAs(wxT("htm"), false) || ext.IsSameAs(_T("html"), false) )
126            return wxT("text/html");
127        if ( ext.IsSameAs(wxT("jpg"), false) || ext.IsSameAs(_T("jpeg"), false) )
128            return wxT("image/jpeg");
129        if ( ext.IsSameAs(wxT("gif"), false) )
130            return wxT("image/gif");
131        if ( ext.IsSameAs(wxT("png"), false) )
132            return wxT("image/png");
133        if ( ext.IsSameAs(wxT("bmp"), false) )
134            return wxT("image/bmp");
135        return wxEmptyString;
136    }
137}
138
139
140
141wxString wxFileSystemHandler::GetProtocol(const wxString& location) const
142{
143    wxString s = wxEmptyString;
144    int i, l = location.length();
145    bool fnd = false;
146
147    for (i = l-1; (i >= 0) && ((location[i] != wxT('#')) || (!fnd)); i--) {
148        if ((location[i] == wxT(':')) && (i != 1 /*win: C:\path*/)) fnd = true;
149    }
150    if (!fnd) return wxT("file");
151    for (++i; (i < l) && (location[i] != wxT(':')); i++) s << location[i];
152    return s;
153}
154
155
156wxString wxFileSystemHandler::GetLeftLocation(const wxString& location) const
157{
158    int i;
159    bool fnd = false;
160
161    for (i = location.length()-1; i >= 0; i--) {
162        if ((location[i] == wxT(':')) && (i != 1 /*win: C:\path*/)) fnd = true;
163        else if (fnd && (location[i] == wxT('#'))) return location.Left(i);
164    }
165    return wxEmptyString;
166}
167
168wxString wxFileSystemHandler::GetRightLocation(const wxString& location) const
169{
170    int i, l = location.length();
171    int l2 = l + 1;
172
173    for (i = l-1;
174         (i >= 0) &&
175         ((location[i] != wxT(':')) || (i == 1) || (location[i-2] == wxT(':')));
176         i--)
177    {
178        if (location[i] == wxT('#')) l2 = i + 1;
179    }
180    if (i == 0) return wxEmptyString;
181    else return location.Mid(i + 1, l2 - i - 2);
182}
183
184wxString wxFileSystemHandler::GetAnchor(const wxString& location) const
185{
186    wxChar c;
187    int l = location.length();
188
189    for (int i = l-1; i >= 0; i--) {
190        c = location[i];
191        if (c == wxT('#'))
192            return location.Right(l-i-1);
193        else if ((c == wxT('/')) || (c == wxT('\\')) || (c == wxT(':')))
194            return wxEmptyString;
195    }
196    return wxEmptyString;
197}
198
199
200wxString wxFileSystemHandler::FindFirst(const wxString& WXUNUSED(spec),
201                                        int WXUNUSED(flags))
202{
203    return wxEmptyString;
204}
205
206wxString wxFileSystemHandler::FindNext()
207{
208    return wxEmptyString;
209}
210
211//--------------------------------------------------------------------------------
212// wxLocalFSHandler
213//--------------------------------------------------------------------------------
214
215
216wxString wxLocalFSHandler::ms_root;
217
218bool wxLocalFSHandler::CanOpen(const wxString& location)
219{
220    return GetProtocol(location) == wxT("file");
221}
222
223wxFSFile* wxLocalFSHandler::OpenFile(wxFileSystem& WXUNUSED(fs), const wxString& location)
224{
225    // location has Unix path separators
226    wxString right = GetRightLocation(location);
227    wxFileName fn = wxFileSystem::URLToFileName(right);
228    wxString fullpath = ms_root + fn.GetFullPath();
229
230    if (!wxFileExists(fullpath))
231        return (wxFSFile*) NULL;
232
233    // we need to check whether we can really read from this file, otherwise
234    // wxFSFile is not going to work
235#if wxUSE_FFILE
236    wxFFileInputStream *is = new wxFFileInputStream(fullpath);
237#elif wxUSE_FILE
238    wxFileInputStream *is = new wxFileInputStream(fullpath);
239#else
240#error One of wxUSE_FILE or wxUSE_FFILE must be set to 1 for wxFSHandler to work
241#endif
242    if ( !is->Ok() )
243    {
244        delete is;
245        return (wxFSFile*) NULL;
246    }
247
248    return new wxFSFile(is,
249                        right,
250                        GetMimeTypeFromExt(location),
251                        GetAnchor(location)
252#if wxUSE_DATETIME
253                        ,wxDateTime(wxFileModificationTime(fullpath))
254#endif // wxUSE_DATETIME
255                        );
256}
257
258wxString wxLocalFSHandler::FindFirst(const wxString& spec, int flags)
259{
260    wxFileName fn = wxFileSystem::URLToFileName(GetRightLocation(spec));
261    return wxFindFirstFile(ms_root + fn.GetFullPath(), flags);
262}
263
264wxString wxLocalFSHandler::FindNext()
265{
266    return wxFindNextFile();
267}
268
269
270
271//-----------------------------------------------------------------------------
272// wxFileSystem
273//-----------------------------------------------------------------------------
274
275IMPLEMENT_DYNAMIC_CLASS(wxFileSystem, wxObject)
276IMPLEMENT_ABSTRACT_CLASS(wxFSFile, wxObject)
277
278
279wxList wxFileSystem::m_Handlers;
280
281
282wxFileSystem::~wxFileSystem()
283{
284    WX_CLEAR_HASH_MAP(wxFSHandlerHash, m_LocalHandlers)
285}
286
287
288static wxString MakeCorrectPath(const wxString& path)
289{
290    wxString p(path);
291    wxString r;
292    int i, j, cnt;
293
294    cnt = p.length();
295    for (i = 0; i < cnt; i++)
296      if (p.GetChar(i) == wxT('\\')) p.GetWritableChar(i) = wxT('/'); // Want to be windows-safe
297
298    if (p.Left(2) == wxT("./")) { p = p.Mid(2); cnt -= 2; }
299
300    if (cnt < 3) return p;
301
302    r << p.GetChar(0) << p.GetChar(1);
303
304    // skip trailing ../.., if any
305    for (i = 2; i < cnt && (p.GetChar(i) == wxT('/') || p.GetChar(i) == wxT('.')); i++) r << p.GetChar(i);
306
307    // remove back references: translate dir1/../dir2 to dir2
308    for (; i < cnt; i++)
309    {
310        r << p.GetChar(i);
311        if (p.GetChar(i) == wxT('/') && p.GetChar(i-1) == wxT('.') && p.GetChar(i-2) == wxT('.'))
312        {
313            for (j = r.length() - 2; j >= 0 && r.GetChar(j) != wxT('/') && r.GetChar(j) != wxT(':'); j--) {}
314            if (j >= 0 && r.GetChar(j) != wxT(':'))
315            {
316                for (j = j - 1; j >= 0 && r.GetChar(j) != wxT('/') && r.GetChar(j) != wxT(':'); j--) {}
317                r.Remove(j + 1);
318            }
319        }
320    }
321
322    for (; i < cnt; i++) r << p.GetChar(i);
323
324    return r;
325}
326
327
328void wxFileSystem::ChangePathTo(const wxString& location, bool is_dir)
329{
330    int i, pathpos = -1;
331
332    m_Path = MakeCorrectPath(location);
333
334    if (is_dir)
335    {
336        if (m_Path.length() > 0 && m_Path.Last() != wxT('/') && m_Path.Last() != wxT(':'))
337            m_Path << wxT('/');
338    }
339
340    else
341    {
342        for (i = m_Path.length()-1; i >= 0; i--)
343        {
344            if (m_Path[(unsigned int) i] == wxT('/'))
345            {
346                if ((i > 1) && (m_Path[(unsigned int) (i-1)] == wxT('/')) && (m_Path[(unsigned int) (i-2)] == wxT(':')))
347                {
348                    i -= 2;
349                    continue;
350                }
351                else
352                {
353                    pathpos = i;
354                    break;
355                }
356            }
357            else if (m_Path[(unsigned int) i] == wxT(':')) {
358                pathpos = i;
359                break;
360            }
361        }
362        if (pathpos == -1)
363        {
364            for (i = 0; i < (int) m_Path.length(); i++)
365            {
366                if (m_Path[(unsigned int) i] == wxT(':'))
367                {
368                    m_Path.Remove(i+1);
369                    break;
370                }
371            }
372            if (i == (int) m_Path.length())
373                m_Path = wxEmptyString;
374        }
375        else
376        {
377            m_Path.Remove(pathpos+1);
378        }
379    }
380}
381
382
383
384wxFileSystemHandler *wxFileSystem::MakeLocal(wxFileSystemHandler *h)
385{
386    wxClassInfo *classinfo = h->GetClassInfo();
387
388    if (classinfo->IsDynamic())
389    {
390        wxFileSystemHandler*& local = m_LocalHandlers[classinfo];
391        if (!local)
392            local = (wxFileSystemHandler*)classinfo->CreateObject();
393        return local;
394    }
395    else
396    {
397        return h;
398    }
399}
400
401
402
403wxFSFile* wxFileSystem::OpenFile(const wxString& location, int flags)
404{
405    if ((flags & wxFS_READ) == 0)
406        return NULL;
407
408    wxString loc = MakeCorrectPath(location);
409    unsigned i, ln;
410    wxChar meta;
411    wxFSFile *s = NULL;
412    wxList::compatibility_iterator node;
413
414    ln = loc.length();
415    meta = 0;
416    for (i = 0; i < ln; i++)
417    {
418        switch (loc[i])
419        {
420            case wxT('/') : case wxT(':') : case wxT('#') :
421                meta = loc[i];
422                break;
423        }
424        if (meta != 0) break;
425    }
426    m_LastName = wxEmptyString;
427
428    // try relative paths first :
429    if (meta != wxT(':'))
430    {
431        node = m_Handlers.GetFirst();
432        while (node)
433        {
434            wxFileSystemHandler *h = (wxFileSystemHandler*) node -> GetData();
435            if (h->CanOpen(m_Path + loc))
436            {
437                s = MakeLocal(h)->OpenFile(*this, m_Path + loc);
438                if (s) { m_LastName = m_Path + loc; break; }
439            }
440            node = node->GetNext();
441        }
442    }
443
444    // if failed, try absolute paths :
445    if (s == NULL)
446    {
447        node = m_Handlers.GetFirst();
448        while (node)
449        {
450            wxFileSystemHandler *h = (wxFileSystemHandler*) node->GetData();
451            if (h->CanOpen(loc))
452            {
453                s = MakeLocal(h)->OpenFile(*this, loc);
454                if (s) { m_LastName = loc; break; }
455            }
456            node = node->GetNext();
457        }
458    }
459
460    if (s && (flags & wxFS_SEEKABLE) != 0 && !s->GetStream()->IsSeekable())
461    {
462        wxBackedInputStream *stream;
463        stream = new wxBackedInputStream(s->DetachStream());
464        stream->FindLength();
465        s->SetStream(stream);
466    }
467
468    return (s);
469}
470
471
472
473wxString wxFileSystem::FindFirst(const wxString& spec, int flags)
474{
475    wxList::compatibility_iterator node;
476    wxString spec2(spec);
477
478    m_FindFileHandler = NULL;
479
480    for (int i = spec2.length()-1; i >= 0; i--)
481        if (spec2[(unsigned int) i] == wxT('\\')) spec2.GetWritableChar(i) = wxT('/'); // Want to be windows-safe
482
483    node = m_Handlers.GetFirst();
484    while (node)
485    {
486        wxFileSystemHandler *h = (wxFileSystemHandler*) node -> GetData();
487        if (h -> CanOpen(m_Path + spec2))
488        {
489            m_FindFileHandler = MakeLocal(h);
490            return m_FindFileHandler -> FindFirst(m_Path + spec2, flags);
491        }
492        node = node->GetNext();
493    }
494
495    node = m_Handlers.GetFirst();
496    while (node)
497    {
498        wxFileSystemHandler *h = (wxFileSystemHandler*) node -> GetData();
499        if (h -> CanOpen(spec2))
500        {
501            m_FindFileHandler = MakeLocal(h);
502            return m_FindFileHandler -> FindFirst(spec2, flags);
503        }
504        node = node->GetNext();
505    }
506
507    return wxEmptyString;
508}
509
510
511
512wxString wxFileSystem::FindNext()
513{
514    if (m_FindFileHandler == NULL) return wxEmptyString;
515    else return m_FindFileHandler -> FindNext();
516}
517
518bool wxFileSystem::FindFileInPath(wxString *pStr,
519                                  const wxChar *path,
520                                  const wxChar *basename)
521{
522    // we assume that it's not empty
523    wxCHECK_MSG( !wxIsEmpty(basename), false,
524                _T("empty file name in wxFileSystem::FindFileInPath"));
525
526    // skip path separator in the beginning of the file name if present
527    if ( wxIsPathSeparator(*basename) )
528       basename++;
529
530    wxStringTokenizer tokenizer(path, wxPATH_SEP);
531    while ( tokenizer.HasMoreTokens() )
532    {
533        wxString strFile = tokenizer.GetNextToken();
534        if ( !wxEndsWithPathSeparator(strFile) )
535            strFile += wxFILE_SEP_PATH;
536        strFile += basename;
537
538        wxFSFile *file = OpenFile(strFile);
539        if ( file )
540        {
541            delete file;
542            *pStr = strFile;
543            return true;
544        }
545    }
546
547    return false;
548}
549
550void wxFileSystem::AddHandler(wxFileSystemHandler *handler)
551{
552    // prepend the handler to the beginning of the list because handlers added
553    // last should have the highest priority to allow overriding them
554    m_Handlers.Insert((size_t)0, handler);
555}
556
557wxFileSystemHandler* wxFileSystem::RemoveHandler(wxFileSystemHandler *handler)
558{
559    // if handler has already been removed (or deleted)
560    // we return NULL. This is by design in case
561    // CleanUpHandlers() is called before RemoveHandler
562    // is called, as we cannot control the order
563    // which modules are unloaded
564    if (!m_Handlers.DeleteObject(handler))
565        return NULL;
566
567    return handler;
568}
569
570
571bool wxFileSystem::HasHandlerForPath(const wxString &location)
572{
573    for ( wxList::compatibility_iterator node = m_Handlers.GetFirst();
574           node; node = node->GetNext() )
575    {
576        wxFileSystemHandler *h = (wxFileSystemHandler*) node->GetData();
577        if (h->CanOpen(location))
578            return true;
579    }
580
581    return false;
582}
583
584void wxFileSystem::CleanUpHandlers()
585{
586    WX_CLEAR_LIST(wxList, m_Handlers);
587}
588
589static const wxString g_unixPathString(wxT("/"));
590static const wxString g_nativePathString(wxFILE_SEP_PATH);
591
592// Returns the native path for a file URL
593wxFileName wxFileSystem::URLToFileName(const wxString& url)
594{
595    wxString path = url;
596
597    if ( path.Find(wxT("file://")) == 0 )
598    {
599        path = path.Mid(7);
600    }
601    else if ( path.Find(wxT("file:")) == 0 )
602    {
603        path = path.Mid(5);
604    }
605    // Remove preceding double slash on Mac Classic
606#if defined(__WXMAC__) && !defined(__UNIX__)
607    else if ( path.Find(wxT("//")) == 0 )
608        path = path.Mid(2);
609#endif
610
611    path = wxURI::Unescape(path);
612
613#ifdef __WXMSW__
614    // file urls either start with a forward slash (local harddisk),
615    // otherwise they have a servername/sharename notation,
616    // which only exists on msw and corresponds to a unc
617    if ( path[0u] == wxT('/') && path [1u] != wxT('/'))
618    {
619        path = path.Mid(1);
620    }
621    else if ( (url.Find(wxT("file://")) == 0) &&
622              (path.Find(wxT('/')) != wxNOT_FOUND) &&
623              (path.length() > 1) && (path[1u] != wxT(':')) )
624    {
625        path = wxT("//") + path;
626    }
627#endif
628
629    path.Replace(g_unixPathString, g_nativePathString);
630
631    return wxFileName(path, wxPATH_NATIVE);
632}
633
634// Returns the file URL for a native path
635wxString wxFileSystem::FileNameToURL(const wxFileName& filename)
636{
637    wxFileName fn = filename;
638    fn.Normalize(wxPATH_NORM_DOTS | wxPATH_NORM_TILDE | wxPATH_NORM_ABSOLUTE);
639    wxString url = fn.GetFullPath(wxPATH_NATIVE);
640
641#ifndef __UNIX__
642    // unc notation, wxMSW
643    if ( url.Find(wxT("\\\\")) == 0 )
644    {
645        url = wxT("//") + url.Mid(2);
646    }
647    else
648    {
649        url = wxT("/") + url;
650#ifdef __WXMAC__
651        url = wxT("/") + url;
652#endif
653
654    }
655#endif
656
657    url.Replace(g_nativePathString, g_unixPathString);
658    url.Replace(wxT("%"), wxT("%25")); // '%'s must be replaced first!
659    url.Replace(wxT("#"), wxT("%23"));
660    url.Replace(wxT(":"), wxT("%3A"));
661    url = wxT("file:") + url;
662    return url;
663}
664
665
666///// Module:
667
668class wxFileSystemModule : public wxModule
669{
670    DECLARE_DYNAMIC_CLASS(wxFileSystemModule)
671
672    public:
673        wxFileSystemModule() :
674            wxModule(),
675            m_handler(NULL)
676        {
677        }
678
679        virtual bool OnInit()
680        {
681            m_handler = new wxLocalFSHandler;
682            wxFileSystem::AddHandler(m_handler);
683            return true;
684        }
685        virtual void OnExit()
686        {
687            delete wxFileSystem::RemoveHandler(m_handler);
688
689            wxFileSystem::CleanUpHandlers();
690        }
691
692    private:
693        wxFileSystemHandler* m_handler;
694
695};
696
697IMPLEMENT_DYNAMIC_CLASS(wxFileSystemModule, wxModule)
698
699#endif
700  // wxUSE_FILESYSTEM
701