1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/common/filename.cpp
3// Purpose:     wxFileName - encapsulates a file path
4// Author:      Robert Roebling, Vadim Zeitlin
5// Modified by:
6// Created:     28.12.2000
7// RCS-ID:      $Id: filename.cpp 66915 2011-02-16 21:46:49Z JS $
8// Copyright:   (c) 2000 Robert Roebling
9// Licence:     wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12/*
13   Here are brief descriptions of the filename formats supported by this class:
14
15   wxPATH_UNIX: standard Unix format, used under Darwin as well, absolute file
16                names have the form:
17                /dir1/dir2/.../dirN/filename, "." and ".." stand for the
18                current and parent directory respectively, "~" is parsed as the
19                user HOME and "~username" as the HOME of that user
20
21   wxPATH_DOS:  DOS/Windows format, absolute file names have the form:
22                drive:\dir1\dir2\...\dirN\filename.ext where drive is a single
23                letter. "." and ".." as for Unix but no "~".
24
25                There are also UNC names of the form \\share\fullpath
26
27   wxPATH_MAC:  Mac OS 8/9 and Mac OS X under CodeWarrior 7 format, absolute file
28                names have the form
29                    volume:dir1:...:dirN:filename
30                and the relative file names are either
31                    :dir1:...:dirN:filename
32                or just
33                    filename
34                (although :filename works as well).
35                Since the volume is just part of the file path, it is not
36                treated like a separate entity as it is done under DOS and
37                VMS, it is just treated as another dir.
38
39   wxPATH_VMS:  VMS native format, absolute file names have the form
40                    <device>:[dir1.dir2.dir3]file.txt
41                or
42                    <device>:[000000.dir1.dir2.dir3]file.txt
43
44                the <device> is the physical device (i.e. disk). 000000 is the
45                root directory on the device which can be omitted.
46
47                Note that VMS uses different separators unlike Unix:
48                 : always after the device. If the path does not contain : than
49                   the default (the device of the current directory) is assumed.
50                 [ start of directory specification
51                 . separator between directory and subdirectory
52                 ] between directory and file
53 */
54
55// ============================================================================
56// declarations
57// ============================================================================
58
59// ----------------------------------------------------------------------------
60// headers
61// ----------------------------------------------------------------------------
62
63// For compilers that support precompilation, includes "wx.h".
64#include "wx/wxprec.h"
65
66#ifdef __BORLANDC__
67#pragma hdrstop
68#endif
69
70#ifndef WX_PRECOMP
71    #ifdef __WXMSW__
72        #include "wx/msw/wrapwin.h" // For GetShort/LongPathName
73    #endif
74    #include "wx/dynarray.h"
75    #include "wx/intl.h"
76    #include "wx/log.h"
77    #include "wx/utils.h"
78#endif
79
80#include "wx/filename.h"
81#include "wx/private/filename.h"
82#include "wx/tokenzr.h"
83#include "wx/config.h"          // for wxExpandEnvVars
84#include "wx/dynlib.h"
85
86#if defined(__WIN32__) && defined(__MINGW32__)
87    #include "wx/msw/gccpriv.h"
88#endif
89
90#ifdef __WXWINCE__
91#include "wx/msw/private.h"
92#endif
93
94#if defined(__WXMAC__)
95  #include  "wx/mac/private.h"  // includes mac headers
96#endif
97
98// utime() is POSIX so should normally be available on all Unices
99#ifdef __UNIX_LIKE__
100#include <sys/types.h>
101#include <utime.h>
102#include <sys/stat.h>
103#include <unistd.h>
104#endif
105
106#ifdef __DJGPP__
107#include <unistd.h>
108#endif
109
110#ifdef __MWERKS__
111#ifdef __MACH__
112#include <sys/types.h>
113#include <utime.h>
114#include <sys/stat.h>
115#include <unistd.h>
116#else
117#include <stat.h>
118#include <unistd.h>
119#include <unix.h>
120#endif
121#endif
122
123#ifdef __WATCOMC__
124#include <io.h>
125#include <sys/utime.h>
126#include <sys/stat.h>
127#endif
128
129#ifdef __VISAGECPP__
130#ifndef MAX_PATH
131#define MAX_PATH 256
132#endif
133#endif
134
135#ifdef __EMX__
136#include <os2.h>
137#define MAX_PATH _MAX_PATH
138#endif
139
140
141wxULongLong wxInvalidSize = (unsigned)-1;
142
143
144// ----------------------------------------------------------------------------
145// private classes
146// ----------------------------------------------------------------------------
147
148// small helper class which opens and closes the file - we use it just to get
149// a file handle for the given file name to pass it to some Win32 API function
150#if defined(__WIN32__) && !defined(__WXMICROWIN__)
151
152class wxFileHandle
153{
154public:
155    enum OpenMode
156    {
157        Read,
158        Write
159    };
160
161    wxFileHandle(const wxString& filename, OpenMode mode, int flags = 0)
162    {
163        m_hFile = ::CreateFile
164                    (
165                     filename,                      // name
166                     mode == Read ? GENERIC_READ    // access mask
167                                  : GENERIC_WRITE,
168                     FILE_SHARE_READ |              // sharing mode
169                     FILE_SHARE_WRITE,              // (allow everything)
170                     NULL,                          // no secutity attr
171                     OPEN_EXISTING,                 // creation disposition
172                     flags,                         // flags
173                     NULL                           // no template file
174                    );
175
176        if ( m_hFile == INVALID_HANDLE_VALUE )
177        {
178            wxLogSysError(_("Failed to open '%s' for %s"),
179                          filename.c_str(),
180                          mode == Read ? _("reading") : _("writing"));
181        }
182    }
183
184    ~wxFileHandle()
185    {
186        if ( m_hFile != INVALID_HANDLE_VALUE )
187        {
188            if ( !::CloseHandle(m_hFile) )
189            {
190                wxLogSysError(_("Failed to close file handle"));
191            }
192        }
193    }
194
195    // return true only if the file could be opened successfully
196    bool IsOk() const { return m_hFile != INVALID_HANDLE_VALUE; }
197
198    // get the handle
199    operator HANDLE() const { return m_hFile; }
200
201private:
202    HANDLE m_hFile;
203};
204
205#endif // __WIN32__
206
207// ----------------------------------------------------------------------------
208// private functions
209// ----------------------------------------------------------------------------
210
211#if wxUSE_DATETIME && defined(__WIN32__) && !defined(__WXMICROWIN__)
212
213// convert between wxDateTime and FILETIME which is a 64-bit value representing
214// the number of 100-nanosecond intervals since January 1, 1601.
215
216static void ConvertFileTimeToWx(wxDateTime *dt, const FILETIME &ft)
217{
218    FILETIME ftcopy = ft;
219    FILETIME ftLocal;
220    if ( !::FileTimeToLocalFileTime(&ftcopy, &ftLocal) )
221    {
222        wxLogLastError(_T("FileTimeToLocalFileTime"));
223    }
224
225    SYSTEMTIME st;
226    if ( !::FileTimeToSystemTime(&ftLocal, &st) )
227    {
228        wxLogLastError(_T("FileTimeToSystemTime"));
229    }
230
231    dt->Set(st.wDay, wxDateTime::Month(st.wMonth - 1), st.wYear,
232            st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
233}
234
235static void ConvertWxToFileTime(FILETIME *ft, const wxDateTime& dt)
236{
237    SYSTEMTIME st;
238    st.wDay = dt.GetDay();
239    st.wMonth = (WORD)(dt.GetMonth() + 1);
240    st.wYear = (WORD)dt.GetYear();
241    st.wHour = dt.GetHour();
242    st.wMinute = dt.GetMinute();
243    st.wSecond = dt.GetSecond();
244    st.wMilliseconds = dt.GetMillisecond();
245
246    FILETIME ftLocal;
247    if ( !::SystemTimeToFileTime(&st, &ftLocal) )
248    {
249        wxLogLastError(_T("SystemTimeToFileTime"));
250    }
251
252    if ( !::LocalFileTimeToFileTime(&ftLocal, ft) )
253    {
254        wxLogLastError(_T("LocalFileTimeToFileTime"));
255    }
256}
257
258#endif // wxUSE_DATETIME && __WIN32__
259
260// return a string with the volume par
261static wxString wxGetVolumeString(const wxString& volume, wxPathFormat format)
262{
263    wxString path;
264
265    if ( !volume.empty() )
266    {
267        format = wxFileName::GetFormat(format);
268
269        // Special Windows UNC paths hack, part 2: undo what we did in
270        // SplitPath() and make an UNC path if we have a drive which is not a
271        // single letter (hopefully the network shares can't be one letter only
272        // although I didn't find any authoritative docs on this)
273        if ( format == wxPATH_DOS && volume.length() > 1 )
274        {
275            path << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_DOS << volume;
276        }
277        else if  ( format == wxPATH_DOS || format == wxPATH_VMS )
278        {
279            path << volume << wxFileName::GetVolumeSeparator(format);
280        }
281        // else ignore
282    }
283
284    return path;
285}
286
287// return true if the character is a DOS path separator i.e. either a slash or
288// a backslash
289inline bool IsDOSPathSep(wxChar ch)
290{
291    return ch == wxFILE_SEP_PATH_DOS || ch == wxFILE_SEP_PATH_UNIX;
292}
293
294// return true if the format used is the DOS/Windows one and the string looks
295// like a UNC path
296static bool IsUNCPath(const wxString& path, wxPathFormat format)
297{
298    return format == wxPATH_DOS &&
299                path.length() >= 4 && // "\\a" can't be a UNC path
300                    IsDOSPathSep(path[0u]) &&
301                        IsDOSPathSep(path[1u]) &&
302                            !IsDOSPathSep(path[2u]);
303}
304
305// ============================================================================
306// implementation
307// ============================================================================
308
309// ----------------------------------------------------------------------------
310// wxFileName construction
311// ----------------------------------------------------------------------------
312
313void wxFileName::Assign( const wxFileName &filepath )
314{
315    if ( &filepath == this )
316        return;
317
318    m_volume = filepath.GetVolume();
319    m_dirs = filepath.GetDirs();
320    m_name = filepath.GetName();
321    m_ext = filepath.GetExt();
322    m_relative = filepath.m_relative;
323    m_hasExt = filepath.m_hasExt;
324}
325
326void wxFileName::Assign(const wxString& volume,
327                        const wxString& path,
328                        const wxString& name,
329                        const wxString& ext,
330                        bool hasExt,
331                        wxPathFormat format)
332{
333    // we should ignore paths which look like UNC shares because we already
334    // have the volume here and the UNC notation (\\server\path) is only valid
335    // for paths which don't start with a volume, so prevent SetPath() from
336    // recognizing "\\foo\bar" in "c:\\foo\bar" as an UNC path
337    //
338    // note also that this is a rather ugly way to do what we want (passing
339    // some kind of flag telling to ignore UNC paths to SetPath() would be
340    // better) but this is the safest thing to do to avoid breaking backwards
341    // compatibility in 2.8
342    if ( IsUNCPath(path, format) )
343    {
344        // remove one of the 2 leading backslashes to ensure that it's not
345        // recognized as an UNC path by SetPath()
346        wxString pathNonUNC(path, 1, wxString::npos);
347        SetPath(pathNonUNC, format);
348    }
349    else // no UNC complications
350    {
351        SetPath(path, format);
352    }
353
354    m_volume = volume;
355    m_ext = ext;
356    m_name = name;
357
358    m_hasExt = hasExt;
359}
360
361void wxFileName::SetPath( const wxString& pathOrig, wxPathFormat format )
362{
363    m_dirs.Clear();
364
365    if ( pathOrig.empty() )
366    {
367        // no path at all
368        m_relative = true;
369
370        return;
371    }
372
373    format = GetFormat( format );
374
375    // 0) deal with possible volume part first
376    wxString volume,
377             path;
378    SplitVolume(pathOrig, &volume, &path, format);
379    if ( !volume.empty() )
380    {
381        m_relative = false;
382
383        SetVolume(volume);
384    }
385
386    // 1) Determine if the path is relative or absolute.
387    wxChar leadingChar = path[0u];
388
389    switch (format)
390    {
391        case wxPATH_MAC:
392            m_relative = leadingChar == wxT(':');
393
394            // We then remove a leading ":". The reason is in our
395            // storage form for relative paths:
396            // ":dir:file.txt" actually means "./dir/file.txt" in
397            // DOS notation and should get stored as
398            // (relative) (dir) (file.txt)
399            // "::dir:file.txt" actually means "../dir/file.txt"
400            // stored as (relative) (..) (dir) (file.txt)
401            // This is important only for the Mac as an empty dir
402            // actually means <UP>, whereas under DOS, double
403            // slashes can be ignored: "\\\\" is the same as "\\".
404            if (m_relative)
405                path.erase( 0, 1 );
406            break;
407
408        case wxPATH_VMS:
409            // TODO: what is the relative path format here?
410            m_relative = false;
411            break;
412
413        default:
414            wxFAIL_MSG( _T("Unknown path format") );
415            // !! Fall through !!
416
417        case wxPATH_UNIX:
418            // the paths of the form "~" or "~username" are absolute
419            m_relative = leadingChar != wxT('/') && leadingChar != _T('~');
420            break;
421
422        case wxPATH_DOS:
423            m_relative = !IsPathSeparator(leadingChar, format);
424            break;
425
426    }
427
428    // 2) Break up the path into its members. If the original path
429    //    was just "/" or "\\", m_dirs will be empty. We know from
430    //    the m_relative field, if this means "nothing" or "root dir".
431
432    wxStringTokenizer tn( path, GetPathSeparators(format) );
433
434    while ( tn.HasMoreTokens() )
435    {
436        wxString token = tn.GetNextToken();
437
438        // Remove empty token under DOS and Unix, interpret them
439        // as .. under Mac.
440        if (token.empty())
441        {
442            if (format == wxPATH_MAC)
443                m_dirs.Add( wxT("..") );
444            // else ignore
445        }
446        else
447        {
448           m_dirs.Add( token );
449        }
450    }
451}
452
453void wxFileName::Assign(const wxString& fullpath,
454                        wxPathFormat format)
455{
456    wxString volume, path, name, ext;
457    bool hasExt;
458    SplitPath(fullpath, &volume, &path, &name, &ext, &hasExt, format);
459
460    Assign(volume, path, name, ext, hasExt, format);
461}
462
463void wxFileName::Assign(const wxString& fullpathOrig,
464                        const wxString& fullname,
465                        wxPathFormat format)
466{
467    // always recognize fullpath as directory, even if it doesn't end with a
468    // slash
469    wxString fullpath = fullpathOrig;
470    if ( !fullpath.empty() && !wxEndsWithPathSeparator(fullpath) )
471    {
472        fullpath += GetPathSeparator(format);
473    }
474
475    wxString volume, path, name, ext;
476    bool hasExt;
477
478    // do some consistency checks in debug mode: the name should be really just
479    // the filename and the path should be really just a path
480#ifdef __WXDEBUG__
481    wxString volDummy, pathDummy, nameDummy, extDummy;
482
483    SplitPath(fullname, &volDummy, &pathDummy, &name, &ext, &hasExt, format);
484
485    wxASSERT_MSG( volDummy.empty() && pathDummy.empty(),
486                  _T("the file name shouldn't contain the path") );
487
488    SplitPath(fullpath, &volume, &path, &nameDummy, &extDummy, format);
489
490    wxASSERT_MSG( nameDummy.empty() && extDummy.empty(),
491                  _T("the path shouldn't contain file name nor extension") );
492
493#else // !__WXDEBUG__
494    SplitPath(fullname, NULL /* no volume */, NULL /* no path */,
495                        &name, &ext, &hasExt, format);
496    SplitPath(fullpath, &volume, &path, NULL, NULL, format);
497#endif // __WXDEBUG__/!__WXDEBUG__
498
499    Assign(volume, path, name, ext, hasExt, format);
500}
501
502void wxFileName::Assign(const wxString& pathOrig,
503                        const wxString& name,
504                        const wxString& ext,
505                        wxPathFormat format)
506{
507    wxString volume,
508             path;
509    SplitVolume(pathOrig, &volume, &path, format);
510
511    Assign(volume, path, name, ext, format);
512}
513
514void wxFileName::AssignDir(const wxString& dir, wxPathFormat format)
515{
516    Assign(dir, wxEmptyString, format);
517}
518
519void wxFileName::Clear()
520{
521    m_dirs.Clear();
522
523    m_volume =
524    m_name =
525    m_ext = wxEmptyString;
526
527    // we don't have any absolute path for now
528    m_relative = true;
529
530    // nor any extension
531    m_hasExt = false;
532}
533
534/* static */
535wxFileName wxFileName::FileName(const wxString& file, wxPathFormat format)
536{
537    return wxFileName(file, format);
538}
539
540/* static */
541wxFileName wxFileName::DirName(const wxString& dir, wxPathFormat format)
542{
543    wxFileName fn;
544    fn.AssignDir(dir, format);
545    return fn;
546}
547
548// ----------------------------------------------------------------------------
549// existence tests
550// ----------------------------------------------------------------------------
551
552bool wxFileName::FileExists() const
553{
554    return wxFileName::FileExists( GetFullPath() );
555}
556
557bool wxFileName::FileExists( const wxString &file )
558{
559    return ::wxFileExists( file );
560}
561
562bool wxFileName::DirExists() const
563{
564    return wxFileName::DirExists( GetPath() );
565}
566
567bool wxFileName::DirExists( const wxString &dir )
568{
569    return ::wxDirExists( dir );
570}
571
572// ----------------------------------------------------------------------------
573// CWD and HOME stuff
574// ----------------------------------------------------------------------------
575
576void wxFileName::AssignCwd(const wxString& volume)
577{
578    AssignDir(wxFileName::GetCwd(volume));
579}
580
581/* static */
582wxString wxFileName::GetCwd(const wxString& volume)
583{
584    // if we have the volume, we must get the current directory on this drive
585    // and to do this we have to chdir to this volume - at least under Windows,
586    // I don't know how to get the current drive on another volume elsewhere
587    // (TODO)
588    wxString cwdOld;
589    if ( !volume.empty() )
590    {
591        cwdOld = wxGetCwd();
592        SetCwd(volume + GetVolumeSeparator());
593    }
594
595    wxString cwd = ::wxGetCwd();
596
597    if ( !volume.empty() )
598    {
599        SetCwd(cwdOld);
600    }
601
602    return cwd;
603}
604
605bool wxFileName::SetCwd()
606{
607    return wxFileName::SetCwd( GetPath() );
608}
609
610bool wxFileName::SetCwd( const wxString &cwd )
611{
612    return ::wxSetWorkingDirectory( cwd );
613}
614
615void wxFileName::AssignHomeDir()
616{
617    AssignDir(wxFileName::GetHomeDir());
618}
619
620wxString wxFileName::GetHomeDir()
621{
622    return ::wxGetHomeDir();
623}
624
625
626// ----------------------------------------------------------------------------
627// CreateTempFileName
628// ----------------------------------------------------------------------------
629
630#if wxUSE_FILE || wxUSE_FFILE
631
632
633#if !defined wx_fdopen && defined HAVE_FDOPEN
634    #define wx_fdopen fdopen
635#endif
636
637// NB: GetTempFileName() under Windows creates the file, so using
638//     O_EXCL there would fail
639#ifdef __WINDOWS__
640    #define wxOPEN_EXCL 0
641#else
642    #define wxOPEN_EXCL O_EXCL
643#endif
644
645
646#ifdef wxOpenOSFHandle
647#define WX_HAVE_DELETE_ON_CLOSE
648// On Windows create a file with the FILE_FLAGS_DELETE_ON_CLOSE flags.
649//
650static int wxOpenWithDeleteOnClose(const wxString& filename)
651{
652    DWORD access = GENERIC_READ | GENERIC_WRITE;
653
654    DWORD disposition = OPEN_ALWAYS;
655
656    DWORD attributes = FILE_ATTRIBUTE_TEMPORARY |
657                       FILE_FLAG_DELETE_ON_CLOSE;
658
659    HANDLE h = ::CreateFile(filename, access, 0, NULL,
660                            disposition, attributes, NULL);
661
662    return wxOpenOSFHandle(h, wxO_BINARY);
663}
664#endif // wxOpenOSFHandle
665
666
667// Helper to open the file
668//
669static int wxTempOpen(const wxString& path, bool *deleteOnClose)
670{
671#ifdef WX_HAVE_DELETE_ON_CLOSE
672    if (*deleteOnClose)
673        return wxOpenWithDeleteOnClose(path);
674#endif
675
676    *deleteOnClose = false;
677
678    return wxOpen(path, wxO_BINARY | O_RDWR | O_CREAT | wxOPEN_EXCL, 0600);
679}
680
681
682#if wxUSE_FFILE
683// Helper to open the file and attach it to the wxFFile
684//
685static bool wxTempOpen(wxFFile *file, const wxString& path, bool *deleteOnClose)
686{
687#ifndef wx_fdopen
688    *deleteOnClose = false;
689    return file->Open(path, _T("w+b"));
690#else // wx_fdopen
691    int fd = wxTempOpen(path, deleteOnClose);
692    if (fd == -1)
693        return false;
694    file->Attach(wx_fdopen(fd, "w+b"));
695    return file->IsOpened();
696#endif // wx_fdopen
697}
698#endif // wxUSE_FFILE
699
700
701#if !wxUSE_FILE
702    #define WXFILEARGS(x, y) y
703#elif !wxUSE_FFILE
704    #define WXFILEARGS(x, y) x
705#else
706    #define WXFILEARGS(x, y) x, y
707#endif
708
709
710// Implementation of wxFileName::CreateTempFileName().
711//
712static wxString wxCreateTempImpl(
713        const wxString& prefix,
714        WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp),
715        bool *deleteOnClose = NULL)
716{
717#if wxUSE_FILE && wxUSE_FFILE
718    wxASSERT(fileTemp == NULL || ffileTemp == NULL);
719#endif
720    wxString path, dir, name;
721    bool wantDeleteOnClose = false;
722
723    if (deleteOnClose)
724    {
725        // set the result to false initially
726        wantDeleteOnClose = *deleteOnClose;
727        *deleteOnClose = false;
728    }
729    else
730    {
731        // easier if it alwasys points to something
732        deleteOnClose = &wantDeleteOnClose;
733    }
734
735    // use the directory specified by the prefix
736    wxFileName::SplitPath(prefix, &dir, &name, NULL /* extension */);
737
738    if (dir.empty())
739    {
740        dir = wxFileName::GetTempDir();
741    }
742
743#if defined(__WXWINCE__)
744    path = dir + wxT("\\") + name;
745    int i = 1;
746    while (wxFileName::FileExists(path))
747    {
748        path = dir + wxT("\\") + name ;
749        path << i;
750        i ++;
751    }
752
753#elif defined(__WINDOWS__) && !defined(__WXMICROWIN__)
754    if ( !::GetTempFileName(dir, name, 0, wxStringBuffer(path, MAX_PATH + 1)) )
755    {
756        wxLogLastError(_T("GetTempFileName"));
757
758        path.clear();
759    }
760
761#else // !Windows
762    path = dir;
763
764    if ( !wxEndsWithPathSeparator(dir) &&
765            (name.empty() || !wxIsPathSeparator(name[0u])) )
766    {
767        path += wxFILE_SEP_PATH;
768    }
769
770    path += name;
771
772#if defined(HAVE_MKSTEMP)
773    // scratch space for mkstemp()
774    path += _T("XXXXXX");
775
776    // we need to copy the path to the buffer in which mkstemp() can modify it
777    wxCharBuffer buf( wxConvFile.cWX2MB( path ) );
778
779    // cast is safe because the string length doesn't change
780    int fdTemp = mkstemp( (char*)(const char*) buf );
781    if ( fdTemp == -1 )
782    {
783        // this might be not necessary as mkstemp() on most systems should have
784        // already done it but it doesn't hurt neither...
785        path.clear();
786    }
787    else // mkstemp() succeeded
788    {
789        path = wxConvFile.cMB2WX( (const char*) buf );
790
791    #if wxUSE_FILE
792        // avoid leaking the fd
793        if ( fileTemp )
794        {
795            fileTemp->Attach(fdTemp);
796        }
797        else
798    #endif
799
800    #if wxUSE_FFILE
801        if ( ffileTemp )
802        {
803        #ifdef wx_fdopen
804            ffileTemp->Attach(wx_fdopen(fdTemp, "r+b"));
805        #else
806            ffileTemp->Open(path, _T("r+b"));
807            close(fdTemp);
808        #endif
809        }
810        else
811    #endif
812
813        {
814            close(fdTemp);
815        }
816    }
817#else // !HAVE_MKSTEMP
818
819#ifdef HAVE_MKTEMP
820    // same as above
821    path += _T("XXXXXX");
822
823    wxCharBuffer buf = wxConvFile.cWX2MB( path );
824    if ( !mktemp( (char*)(const char*) buf ) )
825    {
826        path.clear();
827    }
828    else
829    {
830        path = wxConvFile.cMB2WX( (const char*) buf );
831    }
832#else // !HAVE_MKTEMP (includes __DOS__)
833    // generate the unique file name ourselves
834    #if !defined(__DOS__) && !defined(__PALMOS__) && (!defined(__MWERKS__) || defined(__DARWIN__) )
835    path << (unsigned int)getpid();
836    #endif
837
838    wxString pathTry;
839
840    static const size_t numTries = 1000;
841    for ( size_t n = 0; n < numTries; n++ )
842    {
843        // 3 hex digits is enough for numTries == 1000 < 4096
844        pathTry = path + wxString::Format(_T("%.03x"), (unsigned int) n);
845        if ( !wxFileName::FileExists(pathTry) )
846        {
847            break;
848        }
849
850        pathTry.clear();
851    }
852
853    path = pathTry;
854#endif // HAVE_MKTEMP/!HAVE_MKTEMP
855
856#endif // HAVE_MKSTEMP/!HAVE_MKSTEMP
857
858#endif // Windows/!Windows
859
860    if ( path.empty() )
861    {
862        wxLogSysError(_("Failed to create a temporary file name"));
863    }
864    else
865    {
866        bool ok = true;
867
868        // open the file - of course, there is a race condition here, this is
869        // why we always prefer using mkstemp()...
870    #if wxUSE_FILE
871        if ( fileTemp && !fileTemp->IsOpened() )
872        {
873            *deleteOnClose = wantDeleteOnClose;
874            int fd = wxTempOpen(path, deleteOnClose);
875            if (fd != -1)
876                fileTemp->Attach(fd);
877            else
878                ok = false;
879        }
880    #endif
881
882    #if wxUSE_FFILE
883        if ( ffileTemp && !ffileTemp->IsOpened() )
884        {
885            *deleteOnClose = wantDeleteOnClose;
886            ok = wxTempOpen(ffileTemp, path, deleteOnClose);
887        }
888    #endif
889
890        if ( !ok )
891        {
892            // FIXME: If !ok here should we loop and try again with another
893            //        file name?  That is the standard recourse if open(O_EXCL)
894            //        fails, though of course it should be protected against
895            //        possible infinite looping too.
896
897            wxLogError(_("Failed to open temporary file."));
898
899            path.clear();
900        }
901    }
902
903    return path;
904}
905
906
907static bool wxCreateTempImpl(
908        const wxString& prefix,
909        WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp),
910        wxString *name)
911{
912    bool deleteOnClose = true;
913
914    *name = wxCreateTempImpl(prefix,
915                             WXFILEARGS(fileTemp, ffileTemp),
916                             &deleteOnClose);
917
918    bool ok = !name->empty();
919
920    if (deleteOnClose)
921        name->clear();
922#ifdef __UNIX__
923    else if (ok && wxRemoveFile(*name))
924        name->clear();
925#endif
926
927    return ok;
928}
929
930
931static void wxAssignTempImpl(
932        wxFileName *fn,
933        const wxString& prefix,
934        WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp))
935{
936    wxString tempname;
937    tempname = wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, ffileTemp));
938
939    if ( tempname.empty() )
940    {
941        // error, failed to get temp file name
942        fn->Clear();
943    }
944    else // ok
945    {
946        fn->Assign(tempname);
947    }
948}
949
950
951void wxFileName::AssignTempFileName(const wxString& prefix)
952{
953    wxAssignTempImpl(this, prefix, WXFILEARGS(NULL, NULL));
954}
955
956/* static */
957wxString wxFileName::CreateTempFileName(const wxString& prefix)
958{
959    return wxCreateTempImpl(prefix, WXFILEARGS(NULL, NULL));
960}
961
962#endif // wxUSE_FILE || wxUSE_FFILE
963
964
965#if wxUSE_FILE
966
967wxString wxCreateTempFileName(const wxString& prefix,
968                              wxFile *fileTemp,
969                              bool *deleteOnClose)
970{
971    return wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, NULL), deleteOnClose);
972}
973
974bool wxCreateTempFile(const wxString& prefix,
975                      wxFile *fileTemp,
976                      wxString *name)
977{
978    return wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, NULL), name);
979}
980
981void wxFileName::AssignTempFileName(const wxString& prefix, wxFile *fileTemp)
982{
983    wxAssignTempImpl(this, prefix, WXFILEARGS(fileTemp, NULL));
984}
985
986/* static */
987wxString
988wxFileName::CreateTempFileName(const wxString& prefix, wxFile *fileTemp)
989{
990    return wxCreateTempFileName(prefix, fileTemp);
991}
992
993#endif // wxUSE_FILE
994
995
996#if wxUSE_FFILE
997
998wxString wxCreateTempFileName(const wxString& prefix,
999                              wxFFile *fileTemp,
1000                              bool *deleteOnClose)
1001{
1002    return wxCreateTempImpl(prefix, WXFILEARGS(NULL, fileTemp), deleteOnClose);
1003}
1004
1005bool wxCreateTempFile(const wxString& prefix,
1006                      wxFFile *fileTemp,
1007                      wxString *name)
1008{
1009    return wxCreateTempImpl(prefix, WXFILEARGS(NULL, fileTemp), name);
1010
1011}
1012
1013void wxFileName::AssignTempFileName(const wxString& prefix, wxFFile *fileTemp)
1014{
1015    wxAssignTempImpl(this, prefix, WXFILEARGS(NULL, fileTemp));
1016}
1017
1018/* static */
1019wxString
1020wxFileName::CreateTempFileName(const wxString& prefix, wxFFile *fileTemp)
1021{
1022    return wxCreateTempFileName(prefix, fileTemp);
1023}
1024
1025#endif // wxUSE_FFILE
1026
1027
1028// ----------------------------------------------------------------------------
1029// directory operations
1030// ----------------------------------------------------------------------------
1031
1032wxString wxFileName::GetTempDir()
1033{
1034    wxString dir;
1035    dir = wxGetenv(_T("TMPDIR"));
1036    if (dir.empty())
1037    {
1038        dir = wxGetenv(_T("TMP"));
1039        if (dir.empty())
1040        {
1041            dir = wxGetenv(_T("TEMP"));
1042        }
1043    }
1044
1045#if defined(__WXWINCE__)
1046    if (dir.empty())
1047    {
1048        // FIXME. Create \temp dir?
1049        if (DirExists(wxT("\\temp")))
1050            dir = wxT("\\temp");
1051    }
1052#elif defined(__WINDOWS__) && !defined(__WXMICROWIN__)
1053
1054    if ( dir.empty() )
1055    {
1056        if ( !::GetTempPath(MAX_PATH, wxStringBuffer(dir, MAX_PATH + 1)) )
1057        {
1058            wxLogLastError(_T("GetTempPath"));
1059        }
1060
1061        if ( dir.empty() )
1062        {
1063            // GetTempFileName() fails if we pass it an empty string
1064            dir = _T('.');
1065        }
1066    }
1067#else // !Windows
1068
1069    if ( dir.empty() )
1070    {
1071        // default
1072#if defined(__DOS__) || defined(__OS2__)
1073        dir = _T(".");
1074#elif defined(__WXMAC__)
1075        dir = wxMacFindFolder(short(kOnSystemDisk), kTemporaryFolderType, kCreateFolder);
1076#else
1077        dir = _T("/tmp");
1078#endif
1079    }
1080#endif
1081
1082    return dir;
1083}
1084
1085bool wxFileName::Mkdir( int perm, int flags )
1086{
1087    return wxFileName::Mkdir(GetPath(), perm, flags);
1088}
1089
1090bool wxFileName::Mkdir( const wxString& dir, int perm, int flags )
1091{
1092    if ( flags & wxPATH_MKDIR_FULL )
1093    {
1094        // split the path in components
1095        wxFileName filename;
1096        filename.AssignDir(dir);
1097
1098        wxString currPath;
1099        if ( filename.HasVolume())
1100        {
1101            currPath << wxGetVolumeString(filename.GetVolume(), wxPATH_NATIVE);
1102        }
1103
1104        wxArrayString dirs = filename.GetDirs();
1105        size_t count = dirs.GetCount();
1106        for ( size_t i = 0; i < count; i++ )
1107        {
1108            if ( i > 0 ||
1109#if defined(__WXMAC__) && !defined(__DARWIN__)
1110            // relative pathnames are exactely the other way round under mac...
1111                !filename.IsAbsolute()
1112#else
1113                filename.IsAbsolute()
1114#endif
1115            )
1116                currPath += wxFILE_SEP_PATH;
1117            currPath += dirs[i];
1118
1119            if (!DirExists(currPath))
1120            {
1121                if (!wxMkdir(currPath, perm))
1122                {
1123                    // no need to try creating further directories
1124                    return false;
1125                }
1126            }
1127        }
1128
1129        return true;
1130
1131    }
1132
1133    return ::wxMkdir( dir, perm );
1134}
1135
1136bool wxFileName::Rmdir()
1137{
1138    return wxFileName::Rmdir( GetPath() );
1139}
1140
1141bool wxFileName::Rmdir( const wxString &dir )
1142{
1143    return ::wxRmdir( dir );
1144}
1145
1146// ----------------------------------------------------------------------------
1147// path normalization
1148// ----------------------------------------------------------------------------
1149
1150bool wxFileName::Normalize(int flags,
1151                           const wxString& cwd,
1152                           wxPathFormat format)
1153{
1154    // deal with env vars renaming first as this may seriously change the path
1155    if ( flags & wxPATH_NORM_ENV_VARS )
1156    {
1157        wxString pathOrig = GetFullPath(format);
1158        wxString path = wxExpandEnvVars(pathOrig);
1159        if ( path != pathOrig )
1160        {
1161            Assign(path);
1162        }
1163    }
1164
1165
1166    // the existing path components
1167    wxArrayString dirs = GetDirs();
1168
1169    // the path to prepend in front to make the path absolute
1170    wxFileName curDir;
1171
1172    format = GetFormat(format);
1173
1174    // set up the directory to use for making the path absolute later
1175    if ( (flags & wxPATH_NORM_ABSOLUTE) && !IsAbsolute(format) )
1176    {
1177        if ( cwd.empty() )
1178        {
1179            curDir.AssignCwd(GetVolume());
1180        }
1181        else // cwd provided
1182        {
1183            curDir.AssignDir(cwd);
1184        }
1185    }
1186
1187    // handle ~ stuff under Unix only
1188    if ( (format == wxPATH_UNIX) && (flags & wxPATH_NORM_TILDE) )
1189    {
1190        if ( !dirs.IsEmpty() )
1191        {
1192            wxString dir = dirs[0u];
1193            if ( !dir.empty() && dir[0u] == _T('~') )
1194            {
1195                // to make the path absolute use the home directory
1196                curDir.AssignDir(wxGetUserHome(dir.c_str() + 1));
1197
1198                // if we are expanding the tilde, then this path
1199                // *should* be already relative (since we checked for
1200                // the tilde only in the first char of the first dir);
1201                // if m_relative==false, it's because it was initialized
1202                // from a string which started with /~; in that case
1203                // we reach this point but then need m_relative=true
1204                // for relative->absolute expansion later
1205                m_relative = true;
1206
1207                dirs.RemoveAt(0u);
1208            }
1209        }
1210    }
1211
1212    // transform relative path into abs one
1213    if ( curDir.IsOk() )
1214    {
1215        // this path may be relative because it doesn't have the volume name
1216        // and still have m_relative=true; in this case we shouldn't modify
1217        // our directory components but just set the current volume
1218        if ( !HasVolume() && curDir.HasVolume() )
1219        {
1220            SetVolume(curDir.GetVolume());
1221
1222            if ( !m_relative )
1223        {
1224                // yes, it was the case - we don't need curDir then
1225                curDir.Clear();
1226            }
1227        }
1228
1229        // finally, prepend curDir to the dirs array
1230        wxArrayString dirsNew = curDir.GetDirs();
1231        WX_PREPEND_ARRAY(dirs, dirsNew);
1232
1233        // if we used e.g. tilde expansion previously and wxGetUserHome didn't
1234        // return for some reason an absolute path, then curDir maybe not be absolute!
1235        if ( curDir.IsAbsolute(format) )
1236        {
1237            // we have prepended an absolute path and thus we are now an absolute
1238            // file name too
1239            m_relative = false;
1240        }
1241        // else if (flags & wxPATH_NORM_ABSOLUTE):
1242        //   should we warn the user that we didn't manage to make the path absolute?
1243    }
1244
1245    // now deal with ".", ".." and the rest
1246    m_dirs.Empty();
1247    size_t count = dirs.GetCount();
1248    for ( size_t n = 0; n < count; n++ )
1249    {
1250        wxString dir = dirs[n];
1251
1252        if ( flags & wxPATH_NORM_DOTS )
1253        {
1254            if ( dir == wxT(".") )
1255            {
1256                // just ignore
1257                continue;
1258            }
1259
1260            if ( dir == wxT("..") )
1261            {
1262                if ( m_dirs.IsEmpty() )
1263                {
1264                    wxLogError(_("The path '%s' contains too many \"..\"!"),
1265                               GetFullPath().c_str());
1266                    return false;
1267                }
1268
1269                m_dirs.RemoveAt(m_dirs.GetCount() - 1);
1270                continue;
1271            }
1272        }
1273
1274        m_dirs.Add(dir);
1275    }
1276
1277#if defined(__WIN32__) && !defined(__WXWINCE__) && wxUSE_OLE
1278    if ( (flags & wxPATH_NORM_SHORTCUT) )
1279    {
1280        wxString filename;
1281        if (GetShortcutTarget(GetFullPath(format), filename))
1282        {
1283            m_relative = false;
1284            Assign(filename);
1285        }
1286    }
1287#endif
1288
1289#if defined(__WIN32__)
1290    if ( (flags & wxPATH_NORM_LONG) && (format == wxPATH_DOS) )
1291    {
1292        Assign(GetLongPath());
1293    }
1294#endif // Win32
1295
1296    // Change case  (this should be kept at the end of the function, to ensure
1297    // that the path doesn't change any more after we normalize its case)
1298    if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) )
1299    {
1300        m_volume.MakeLower();
1301        m_name.MakeLower();
1302        m_ext.MakeLower();
1303
1304        // directory entries must be made lower case as well
1305        count = m_dirs.GetCount();
1306        for ( size_t i = 0; i < count; i++ )
1307        {
1308            m_dirs[i].MakeLower();
1309        }
1310    }
1311
1312    return true;
1313}
1314
1315// ----------------------------------------------------------------------------
1316// get the shortcut target
1317// ----------------------------------------------------------------------------
1318
1319// WinCE (3) doesn't have CLSID_ShellLink, IID_IShellLink definitions.
1320// The .lnk file is a plain text file so it should be easy to
1321// make it work. Hint from Google Groups:
1322// "If you open up a lnk file, you'll see a
1323// number, followed by a pound sign (#), followed by more text. The
1324// number is the number of characters that follows the pound sign. The
1325// characters after the pound sign are the command line (which _can_
1326// include arguments) to be executed. Any path (e.g. \windows\program
1327// files\myapp.exe) that includes spaces needs to be enclosed in
1328// quotation marks."
1329
1330#if defined(__WIN32__) && !defined(__WXWINCE__) && wxUSE_OLE
1331// The following lines are necessary under WinCE
1332// #include "wx/msw/private.h"
1333// #include <ole2.h>
1334#include <shlobj.h>
1335#if defined(__WXWINCE__)
1336#include <shlguid.h>
1337#endif
1338
1339bool wxFileName::GetShortcutTarget(const wxString& shortcutPath,
1340                                   wxString& targetFilename,
1341                                   wxString* arguments)
1342{
1343    wxString path, file, ext;
1344    wxSplitPath(shortcutPath, & path, & file, & ext);
1345
1346    HRESULT hres;
1347    IShellLink* psl;
1348    bool success = false;
1349
1350    // Assume it's not a shortcut if it doesn't end with lnk
1351    if (ext.CmpNoCase(wxT("lnk"))!=0)
1352        return false;
1353
1354    // create a ShellLink object
1355    hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
1356                            IID_IShellLink, (LPVOID*) &psl);
1357
1358    if (SUCCEEDED(hres))
1359    {
1360        IPersistFile* ppf;
1361        hres = psl->QueryInterface( IID_IPersistFile, (LPVOID *) &ppf);
1362        if (SUCCEEDED(hres))
1363        {
1364            WCHAR wsz[MAX_PATH];
1365
1366            MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, shortcutPath.mb_str(), -1, wsz,
1367                                MAX_PATH);
1368
1369            hres = ppf->Load(wsz, 0);
1370            ppf->Release();
1371
1372            if (SUCCEEDED(hres))
1373            {
1374                wxChar buf[2048];
1375                // Wrong prototype in early versions
1376#if defined(__MINGW32__) && !wxCHECK_W32API_VERSION(2, 2)
1377                psl->GetPath((CHAR*) buf, 2048, NULL, SLGP_UNCPRIORITY);
1378#else
1379                psl->GetPath(buf, 2048, NULL, SLGP_UNCPRIORITY);
1380#endif
1381                targetFilename = wxString(buf);
1382                success = (shortcutPath != targetFilename);
1383
1384                psl->GetArguments(buf, 2048);
1385                wxString args(buf);
1386                if (!args.empty() && arguments)
1387                {
1388                    *arguments = args;
1389                }
1390            }
1391        }
1392
1393        psl->Release();
1394    }
1395    return success;
1396}
1397
1398#endif // __WIN32__ && !__WXWINCE__
1399
1400
1401// ----------------------------------------------------------------------------
1402// absolute/relative paths
1403// ----------------------------------------------------------------------------
1404
1405bool wxFileName::IsAbsolute(wxPathFormat format) const
1406{
1407    // if our path doesn't start with a path separator, it's not an absolute
1408    // path
1409    if ( m_relative )
1410        return false;
1411
1412    if ( !GetVolumeSeparator(format).empty() )
1413    {
1414        // this format has volumes and an absolute path must have one, it's not
1415        // enough to have the full path to bean absolute file under Windows
1416        if ( GetVolume().empty() )
1417            return false;
1418    }
1419
1420    return true;
1421}
1422
1423bool wxFileName::MakeRelativeTo(const wxString& pathBase, wxPathFormat format)
1424{
1425    wxFileName fnBase = wxFileName::DirName(pathBase, format);
1426
1427    // get cwd only once - small time saving
1428    wxString cwd = wxGetCwd();
1429    Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, cwd, format);
1430    fnBase.Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, cwd, format);
1431
1432    bool withCase = IsCaseSensitive(format);
1433
1434    // we can't do anything if the files live on different volumes
1435    if ( !GetVolume().IsSameAs(fnBase.GetVolume(), withCase) )
1436    {
1437        // nothing done
1438        return false;
1439    }
1440
1441    // same drive, so we don't need our volume
1442    m_volume.clear();
1443
1444    // remove common directories starting at the top
1445    while ( !m_dirs.IsEmpty() && !fnBase.m_dirs.IsEmpty() &&
1446                m_dirs[0u].IsSameAs(fnBase.m_dirs[0u], withCase) )
1447    {
1448        m_dirs.RemoveAt(0);
1449        fnBase.m_dirs.RemoveAt(0);
1450    }
1451
1452    // add as many ".." as needed
1453    size_t count = fnBase.m_dirs.GetCount();
1454    for ( size_t i = 0; i < count; i++ )
1455    {
1456        m_dirs.Insert(wxT(".."), 0u);
1457    }
1458
1459    if ( format == wxPATH_UNIX || format == wxPATH_DOS )
1460    {
1461        // a directory made relative with respect to itself is '.' under Unix
1462        // and DOS, by definition (but we don't have to insert "./" for the
1463        // files)
1464        if ( m_dirs.IsEmpty() && IsDir() )
1465        {
1466            m_dirs.Add(_T('.'));
1467        }
1468    }
1469
1470    m_relative = true;
1471
1472    // we were modified
1473    return true;
1474}
1475
1476// ----------------------------------------------------------------------------
1477// filename kind tests
1478// ----------------------------------------------------------------------------
1479
1480bool wxFileName::SameAs(const wxFileName& filepath, wxPathFormat format) const
1481{
1482    wxFileName fn1 = *this,
1483               fn2 = filepath;
1484
1485    // get cwd only once - small time saving
1486    wxString cwd = wxGetCwd();
1487    fn1.Normalize(wxPATH_NORM_ALL | wxPATH_NORM_CASE, cwd, format);
1488    fn2.Normalize(wxPATH_NORM_ALL | wxPATH_NORM_CASE, cwd, format);
1489
1490    if ( fn1.GetFullPath() == fn2.GetFullPath() )
1491        return true;
1492
1493    // TODO: compare inodes for Unix, this works even when filenames are
1494    //       different but files are the same (symlinks) (VZ)
1495
1496    return false;
1497}
1498
1499/* static */
1500bool wxFileName::IsCaseSensitive( wxPathFormat format )
1501{
1502    // only Unix filenames are truely case-sensitive
1503    return GetFormat(format) == wxPATH_UNIX;
1504}
1505
1506/* static */
1507wxString wxFileName::GetForbiddenChars(wxPathFormat format)
1508{
1509    // Inits to forbidden characters that are common to (almost) all platforms.
1510    wxString strForbiddenChars = wxT("*?");
1511
1512    // If asserts, wxPathFormat has been changed. In case of a new path format
1513    // addition, the following code might have to be updated.
1514    wxCOMPILE_TIME_ASSERT(wxPATH_MAX == 5, wxPathFormatChanged);
1515    switch ( GetFormat(format) )
1516    {
1517        default :
1518            wxFAIL_MSG( wxT("Unknown path format") );
1519            // !! Fall through !!
1520
1521        case wxPATH_UNIX:
1522            break;
1523
1524        case wxPATH_MAC:
1525            // On a Mac even names with * and ? are allowed (Tested with OS
1526            // 9.2.1 and OS X 10.2.5)
1527            strForbiddenChars = wxEmptyString;
1528            break;
1529
1530        case wxPATH_DOS:
1531            strForbiddenChars += wxT("\\/:\"<>|");
1532            break;
1533
1534        case wxPATH_VMS:
1535            break;
1536    }
1537
1538    return strForbiddenChars;
1539}
1540
1541/* static */
1542wxString wxFileName::GetVolumeSeparator(wxPathFormat WXUNUSED_IN_WINCE(format))
1543{
1544#ifdef __WXWINCE__
1545    return wxEmptyString;
1546#else
1547    wxString sepVol;
1548
1549    if ( (GetFormat(format) == wxPATH_DOS) ||
1550         (GetFormat(format) == wxPATH_VMS) )
1551    {
1552        sepVol = wxFILE_SEP_DSK;
1553    }
1554    //else: leave empty
1555
1556    return sepVol;
1557#endif
1558}
1559
1560/* static */
1561wxString wxFileName::GetPathSeparators(wxPathFormat format)
1562{
1563    wxString seps;
1564    switch ( GetFormat(format) )
1565    {
1566        case wxPATH_DOS:
1567            // accept both as native APIs do but put the native one first as
1568            // this is the one we use in GetFullPath()
1569            seps << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_UNIX;
1570            break;
1571
1572        default:
1573            wxFAIL_MSG( _T("Unknown wxPATH_XXX style") );
1574            // fall through
1575
1576        case wxPATH_UNIX:
1577            seps = wxFILE_SEP_PATH_UNIX;
1578            break;
1579
1580        case wxPATH_MAC:
1581            seps = wxFILE_SEP_PATH_MAC;
1582            break;
1583
1584        case wxPATH_VMS:
1585            seps = wxFILE_SEP_PATH_VMS;
1586            break;
1587    }
1588
1589    return seps;
1590}
1591
1592/* static */
1593wxString wxFileName::GetPathTerminators(wxPathFormat format)
1594{
1595    format = GetFormat(format);
1596
1597    // under VMS the end of the path is ']', not the path separator used to
1598    // separate the components
1599    return format == wxPATH_VMS ? wxString(_T(']')) : GetPathSeparators(format);
1600}
1601
1602/* static */
1603bool wxFileName::IsPathSeparator(wxChar ch, wxPathFormat format)
1604{
1605    // wxString::Find() doesn't work as expected with NUL - it will always find
1606    // it, so test for it separately
1607    return ch != _T('\0') && GetPathSeparators(format).Find(ch) != wxNOT_FOUND;
1608}
1609
1610// ----------------------------------------------------------------------------
1611// path components manipulation
1612// ----------------------------------------------------------------------------
1613
1614/* static */ bool wxFileName::IsValidDirComponent(const wxString& dir)
1615{
1616    if ( dir.empty() )
1617    {
1618        wxFAIL_MSG( _T("empty directory passed to wxFileName::InsertDir()") );
1619
1620        return false;
1621    }
1622
1623    const size_t len = dir.length();
1624    for ( size_t n = 0; n < len; n++ )
1625    {
1626        if ( dir[n] == GetVolumeSeparator() || IsPathSeparator(dir[n]) )
1627        {
1628            wxFAIL_MSG( _T("invalid directory component in wxFileName") );
1629
1630            return false;
1631        }
1632    }
1633
1634    return true;
1635}
1636
1637void wxFileName::AppendDir( const wxString& dir )
1638{
1639    if ( IsValidDirComponent(dir) )
1640        m_dirs.Add( dir );
1641}
1642
1643void wxFileName::PrependDir( const wxString& dir )
1644{
1645    InsertDir(0, dir);
1646}
1647
1648void wxFileName::InsertDir(size_t before, const wxString& dir)
1649{
1650    if ( IsValidDirComponent(dir) )
1651        m_dirs.Insert(dir, before);
1652}
1653
1654void wxFileName::RemoveDir(size_t pos)
1655{
1656    m_dirs.RemoveAt(pos);
1657}
1658
1659// ----------------------------------------------------------------------------
1660// accessors
1661// ----------------------------------------------------------------------------
1662
1663void wxFileName::SetFullName(const wxString& fullname)
1664{
1665    SplitPath(fullname, NULL /* no volume */, NULL /* no path */,
1666                        &m_name, &m_ext, &m_hasExt);
1667}
1668
1669wxString wxFileName::GetFullName() const
1670{
1671    wxString fullname = m_name;
1672    if ( m_hasExt )
1673    {
1674        fullname << wxFILE_SEP_EXT << m_ext;
1675    }
1676
1677    return fullname;
1678}
1679
1680wxString wxFileName::GetPath( int flags, wxPathFormat format ) const
1681{
1682    format = GetFormat( format );
1683
1684    wxString fullpath;
1685
1686    // return the volume with the path as well if requested
1687    if ( flags & wxPATH_GET_VOLUME )
1688    {
1689        fullpath += wxGetVolumeString(GetVolume(), format);
1690    }
1691
1692    // the leading character
1693    switch ( format )
1694    {
1695        case wxPATH_MAC:
1696            if ( m_relative )
1697                fullpath += wxFILE_SEP_PATH_MAC;
1698            break;
1699
1700        case wxPATH_DOS:
1701            if ( !m_relative )
1702                fullpath += wxFILE_SEP_PATH_DOS;
1703            break;
1704
1705        default:
1706            wxFAIL_MSG( wxT("Unknown path format") );
1707            // fall through
1708
1709        case wxPATH_UNIX:
1710            if ( !m_relative )
1711            {
1712                // normally the absolute file names start with a slash
1713                // with one exception: the ones like "~/foo.bar" don't
1714                // have it
1715                if ( m_dirs.IsEmpty() || m_dirs[0u] != _T('~') )
1716                {
1717                    fullpath += wxFILE_SEP_PATH_UNIX;
1718                }
1719            }
1720            break;
1721
1722        case wxPATH_VMS:
1723            // no leading character here but use this place to unset
1724            // wxPATH_GET_SEPARATOR flag: under VMS it doesn't make sense
1725            // as, if I understand correctly, there should never be a dot
1726            // before the closing bracket
1727            flags &= ~wxPATH_GET_SEPARATOR;
1728    }
1729
1730    if ( m_dirs.empty() )
1731    {
1732        // there is nothing more
1733        return fullpath;
1734    }
1735
1736    // then concatenate all the path components using the path separator
1737    if ( format == wxPATH_VMS )
1738    {
1739        fullpath += wxT('[');
1740    }
1741
1742    const size_t dirCount = m_dirs.GetCount();
1743    for ( size_t i = 0; i < dirCount; i++ )
1744    {
1745        switch (format)
1746        {
1747            case wxPATH_MAC:
1748                if ( m_dirs[i] == wxT(".") )
1749                {
1750                    // skip appending ':', this shouldn't be done in this
1751                    // case as "::" is interpreted as ".." under Unix
1752                    continue;
1753                }
1754
1755                // convert back from ".." to nothing
1756                if ( !m_dirs[i].IsSameAs(wxT("..")) )
1757                     fullpath += m_dirs[i];
1758                break;
1759
1760            default:
1761                wxFAIL_MSG( wxT("Unexpected path format") );
1762                // still fall through
1763
1764            case wxPATH_DOS:
1765            case wxPATH_UNIX:
1766                fullpath += m_dirs[i];
1767                break;
1768
1769            case wxPATH_VMS:
1770                // TODO: What to do with ".." under VMS
1771
1772                // convert back from ".." to nothing
1773                if ( !m_dirs[i].IsSameAs(wxT("..")) )
1774                    fullpath += m_dirs[i];
1775                break;
1776        }
1777
1778        if ( (flags & wxPATH_GET_SEPARATOR) || (i != dirCount - 1) )
1779            fullpath += GetPathSeparator(format);
1780    }
1781
1782    if ( format == wxPATH_VMS )
1783    {
1784        fullpath += wxT(']');
1785    }
1786
1787    return fullpath;
1788}
1789
1790wxString wxFileName::GetFullPath( wxPathFormat format ) const
1791{
1792    // we already have a function to get the path
1793    wxString fullpath = GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR,
1794                                format);
1795
1796    // now just add the file name and extension to it
1797    fullpath += GetFullName();
1798
1799    return fullpath;
1800}
1801
1802// Return the short form of the path (returns identity on non-Windows platforms)
1803wxString wxFileName::GetShortPath() const
1804{
1805    wxString path(GetFullPath());
1806
1807#if defined(__WXMSW__) && defined(__WIN32__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
1808    DWORD sz = ::GetShortPathName(path, NULL, 0);
1809    if ( sz != 0 )
1810    {
1811        wxString pathOut;
1812        if ( ::GetShortPathName
1813               (
1814                path,
1815                wxStringBuffer(pathOut, sz),
1816                sz
1817               ) != 0 )
1818        {
1819            return pathOut;
1820        }
1821    }
1822#endif // Windows
1823
1824    return path;
1825}
1826
1827// Return the long form of the path (returns identity on non-Windows platforms)
1828wxString wxFileName::GetLongPath() const
1829{
1830    wxString pathOut,
1831             path = GetFullPath();
1832
1833#if defined(__WIN32__) && !defined(__WXWINCE__) && !defined(__WXMICROWIN__)
1834
1835#if wxUSE_DYNLIB_CLASS
1836    typedef DWORD (WINAPI *GET_LONG_PATH_NAME)(const wxChar *, wxChar *, DWORD);
1837
1838    // this is MT-safe as in the worst case we're going to resolve the function
1839    // twice -- but as the result is the same in both threads, it's ok
1840    static GET_LONG_PATH_NAME s_pfnGetLongPathName = NULL;
1841    if ( !s_pfnGetLongPathName )
1842    {
1843        static bool s_triedToLoad = false;
1844
1845        if ( !s_triedToLoad )
1846        {
1847            s_triedToLoad = true;
1848
1849            wxDynamicLibrary dllKernel(_T("kernel32"));
1850
1851            const wxChar* GetLongPathName = _T("GetLongPathName")
1852#if wxUSE_UNICODE
1853                              _T("W");
1854#else // ANSI
1855                              _T("A");
1856#endif // Unicode/ANSI
1857
1858            if ( dllKernel.HasSymbol(GetLongPathName) )
1859            {
1860                s_pfnGetLongPathName = (GET_LONG_PATH_NAME)
1861                    dllKernel.GetSymbol(GetLongPathName);
1862            }
1863
1864            // note that kernel32.dll can be unloaded, it stays in memory
1865            // anyhow as all Win32 programs link to it and so it's safe to call
1866            // GetLongPathName() even after unloading it
1867        }
1868    }
1869
1870    if ( s_pfnGetLongPathName )
1871    {
1872        DWORD dwSize = (*s_pfnGetLongPathName)(path, NULL, 0);
1873        if ( dwSize > 0 )
1874        {
1875            if ( (*s_pfnGetLongPathName)
1876                 (
1877                  path,
1878                  wxStringBuffer(pathOut, dwSize),
1879                  dwSize
1880                 ) != 0 )
1881            {
1882                return pathOut;
1883            }
1884        }
1885    }
1886#endif // wxUSE_DYNLIB_CLASS
1887
1888    // The OS didn't support GetLongPathName, or some other error.
1889    // We need to call FindFirstFile on each component in turn.
1890
1891    WIN32_FIND_DATA findFileData;
1892    HANDLE hFind;
1893
1894    if ( HasVolume() )
1895        pathOut = GetVolume() +
1896                  GetVolumeSeparator(wxPATH_DOS) +
1897                  GetPathSeparator(wxPATH_DOS);
1898    else
1899        pathOut = wxEmptyString;
1900
1901    wxArrayString dirs = GetDirs();
1902    dirs.Add(GetFullName());
1903
1904    wxString tmpPath;
1905
1906    size_t count = dirs.GetCount();
1907    for ( size_t i = 0; i < count; i++ )
1908    {
1909        // We're using pathOut to collect the long-name path, but using a
1910        // temporary for appending the last path component which may be
1911        // short-name
1912        tmpPath = pathOut + dirs[i];
1913
1914        if ( tmpPath.empty() )
1915            continue;
1916
1917        // can't see this being necessary? MF
1918        if ( tmpPath.Last() == GetVolumeSeparator(wxPATH_DOS) )
1919        {
1920            // Can't pass a drive and root dir to FindFirstFile,
1921            // so continue to next dir
1922            tmpPath += wxFILE_SEP_PATH;
1923            pathOut = tmpPath;
1924            continue;
1925        }
1926
1927        hFind = ::FindFirstFile(tmpPath, &findFileData);
1928        if (hFind == INVALID_HANDLE_VALUE)
1929        {
1930            // Error: most likely reason is that path doesn't exist, so
1931            // append any unprocessed parts and return
1932            for ( i += 1; i < count; i++ )
1933                tmpPath += wxFILE_SEP_PATH + dirs[i];
1934
1935            return tmpPath;
1936        }
1937
1938        pathOut += findFileData.cFileName;
1939        if ( (i < (count-1)) )
1940            pathOut += wxFILE_SEP_PATH;
1941
1942        ::FindClose(hFind);
1943    }
1944#else // !Win32
1945    pathOut = path;
1946#endif // Win32/!Win32
1947
1948    return pathOut;
1949}
1950
1951wxPathFormat wxFileName::GetFormat( wxPathFormat format )
1952{
1953    if (format == wxPATH_NATIVE)
1954    {
1955#if defined(__WXMSW__) || defined(__OS2__) || defined(__DOS__)
1956        format = wxPATH_DOS;
1957#elif defined(__WXMAC__) && !defined(__DARWIN__)
1958        format = wxPATH_MAC;
1959#elif defined(__VMS)
1960        format = wxPATH_VMS;
1961#else
1962        format = wxPATH_UNIX;
1963#endif
1964    }
1965    return format;
1966}
1967
1968// ----------------------------------------------------------------------------
1969// path splitting function
1970// ----------------------------------------------------------------------------
1971
1972/* static */
1973void
1974wxFileName::SplitVolume(const wxString& fullpathWithVolume,
1975                        wxString *pstrVolume,
1976                        wxString *pstrPath,
1977                        wxPathFormat format)
1978{
1979    format = GetFormat(format);
1980
1981    wxString fullpath = fullpathWithVolume;
1982
1983    // special Windows UNC paths hack: transform \\share\path into share:path
1984    if ( IsUNCPath(fullpath, format) )
1985    {
1986        fullpath.erase(0, 2);
1987
1988        size_t posFirstSlash =
1989            fullpath.find_first_of(GetPathTerminators(format));
1990        if ( posFirstSlash != wxString::npos )
1991        {
1992            fullpath[posFirstSlash] = wxFILE_SEP_DSK;
1993
1994            // UNC paths are always absolute, right? (FIXME)
1995            fullpath.insert(posFirstSlash + 1, 1, wxFILE_SEP_PATH_DOS);
1996        }
1997    }
1998
1999    // We separate the volume here
2000    if ( format == wxPATH_DOS || format == wxPATH_VMS )
2001    {
2002        wxString sepVol = GetVolumeSeparator(format);
2003
2004        size_t posFirstColon = fullpath.find_first_of(sepVol);
2005        if ( posFirstColon != wxString::npos )
2006        {
2007            if ( pstrVolume )
2008            {
2009                *pstrVolume = fullpath.Left(posFirstColon);
2010            }
2011
2012            // remove the volume name and the separator from the full path
2013            fullpath.erase(0, posFirstColon + sepVol.length());
2014        }
2015    }
2016
2017    if ( pstrPath )
2018        *pstrPath = fullpath;
2019}
2020
2021/* static */
2022void wxFileName::SplitPath(const wxString& fullpathWithVolume,
2023                           wxString *pstrVolume,
2024                           wxString *pstrPath,
2025                           wxString *pstrName,
2026                           wxString *pstrExt,
2027                           bool *hasExt,
2028                           wxPathFormat format)
2029{
2030    format = GetFormat(format);
2031
2032    wxString fullpath;
2033    SplitVolume(fullpathWithVolume, pstrVolume, &fullpath, format);
2034
2035    // find the positions of the last dot and last path separator in the path
2036    size_t posLastDot = fullpath.find_last_of(wxFILE_SEP_EXT);
2037    size_t posLastSlash = fullpath.find_last_of(GetPathTerminators(format));
2038
2039    // check whether this dot occurs at the very beginning of a path component
2040    if ( (posLastDot != wxString::npos) &&
2041         (posLastDot == 0 ||
2042            IsPathSeparator(fullpath[posLastDot - 1]) ||
2043            (format == wxPATH_VMS && fullpath[posLastDot - 1] == _T(']'))) )
2044    {
2045        // dot may be (and commonly -- at least under Unix -- is) the first
2046        // character of the filename, don't treat the entire filename as
2047        // extension in this case
2048        posLastDot = wxString::npos;
2049    }
2050
2051    // if we do have a dot and a slash, check that the dot is in the name part
2052    if ( (posLastDot != wxString::npos) &&
2053         (posLastSlash != wxString::npos) &&
2054         (posLastDot < posLastSlash) )
2055    {
2056        // the dot is part of the path, not the start of the extension
2057        posLastDot = wxString::npos;
2058    }
2059
2060    // now fill in the variables provided by user
2061    if ( pstrPath )
2062    {
2063        if ( posLastSlash == wxString::npos )
2064        {
2065            // no path at all
2066            pstrPath->Empty();
2067        }
2068        else
2069        {
2070            // take everything up to the path separator but take care to make
2071            // the path equal to something like '/', not empty, for the files
2072            // immediately under root directory
2073            size_t len = posLastSlash;
2074
2075            // this rule does not apply to mac since we do not start with colons (sep)
2076            // except for relative paths
2077            if ( !len && format != wxPATH_MAC)
2078                len++;
2079
2080            *pstrPath = fullpath.Left(len);
2081
2082            // special VMS hack: remove the initial bracket
2083            if ( format == wxPATH_VMS )
2084            {
2085                if ( (*pstrPath)[0u] == _T('[') )
2086                    pstrPath->erase(0, 1);
2087            }
2088        }
2089    }
2090
2091    if ( pstrName )
2092    {
2093        // take all characters starting from the one after the last slash and
2094        // up to, but excluding, the last dot
2095        size_t nStart = posLastSlash == wxString::npos ? 0 : posLastSlash + 1;
2096        size_t count;
2097        if ( posLastDot == wxString::npos )
2098        {
2099            // take all until the end
2100            count = wxString::npos;
2101        }
2102        else if ( posLastSlash == wxString::npos )
2103        {
2104            count = posLastDot;
2105        }
2106        else // have both dot and slash
2107        {
2108            count = posLastDot - posLastSlash - 1;
2109        }
2110
2111        *pstrName = fullpath.Mid(nStart, count);
2112    }
2113
2114    // finally deal with the extension here: we have an added complication that
2115    // extension may be empty (but present) as in "foo." where trailing dot
2116    // indicates the empty extension at the end -- and hence we must remember
2117    // that we have it independently of pstrExt
2118    if ( posLastDot == wxString::npos )
2119    {
2120        // no extension
2121        if ( pstrExt )
2122            pstrExt->clear();
2123        if ( hasExt )
2124            *hasExt = false;
2125    }
2126    else
2127    {
2128        // take everything after the dot
2129        if ( pstrExt )
2130            *pstrExt = fullpath.Mid(posLastDot + 1);
2131        if ( hasExt )
2132            *hasExt = true;
2133    }
2134}
2135
2136/* static */
2137void wxFileName::SplitPath(const wxString& fullpath,
2138                           wxString *path,
2139                           wxString *name,
2140                           wxString *ext,
2141                           wxPathFormat format)
2142{
2143    wxString volume;
2144    SplitPath(fullpath, &volume, path, name, ext, format);
2145
2146    if ( path )
2147    {
2148        path->Prepend(wxGetVolumeString(volume, format));
2149    }
2150}
2151
2152/* static */
2153wxString wxFileName::StripExtension(const wxString& fullpath)
2154{
2155    wxFileName fn(fullpath);
2156    fn.SetExt(_T(""));
2157    return fn.GetFullPath();
2158}
2159
2160// ----------------------------------------------------------------------------
2161// time functions
2162// ----------------------------------------------------------------------------
2163
2164#if wxUSE_DATETIME
2165
2166bool wxFileName::SetTimes(const wxDateTime *dtAccess,
2167                          const wxDateTime *dtMod,
2168                          const wxDateTime *dtCreate)
2169{
2170#if defined(__WIN32__)
2171    FILETIME ftAccess, ftCreate, ftWrite;
2172
2173    if ( dtCreate )
2174        ConvertWxToFileTime(&ftCreate, *dtCreate);
2175    if ( dtAccess )
2176        ConvertWxToFileTime(&ftAccess, *dtAccess);
2177    if ( dtMod )
2178        ConvertWxToFileTime(&ftWrite, *dtMod);
2179
2180    wxString path;
2181    int flags;
2182    if ( IsDir() )
2183    {
2184        if ( wxGetOsVersion() == wxOS_WINDOWS_9X )
2185        {
2186            wxLogError(_("Setting directory access times is not supported under this OS version"));
2187            return false;
2188        }
2189
2190        path = GetPath();
2191        flags = FILE_FLAG_BACKUP_SEMANTICS;
2192    }
2193    else // file
2194    {
2195        path = GetFullPath();
2196        flags = 0;
2197    }
2198
2199    wxFileHandle fh(path, wxFileHandle::Write, flags);
2200    if ( fh.IsOk() )
2201    {
2202        if ( ::SetFileTime(fh,
2203                           dtCreate ? &ftCreate : NULL,
2204                           dtAccess ? &ftAccess : NULL,
2205                           dtMod ? &ftWrite : NULL) )
2206        {
2207            return true;
2208        }
2209    }
2210#elif defined(__UNIX_LIKE__) || (defined(__DOS__) && defined(__WATCOMC__))
2211    wxUnusedVar(dtCreate);
2212
2213    if ( !dtAccess && !dtMod )
2214    {
2215        // can't modify the creation time anyhow, don't try
2216        return true;
2217    }
2218
2219    // if dtAccess or dtMod is not specified, use the other one (which must be
2220    // non NULL because of the test above) for both times
2221    utimbuf utm;
2222    utm.actime = dtAccess ? dtAccess->GetTicks() : dtMod->GetTicks();
2223    utm.modtime = dtMod ? dtMod->GetTicks() : dtAccess->GetTicks();
2224    if ( utime(GetFullPath().fn_str(), &utm) == 0 )
2225    {
2226        return true;
2227    }
2228#else // other platform
2229    wxUnusedVar(dtAccess);
2230    wxUnusedVar(dtMod);
2231    wxUnusedVar(dtCreate);
2232#endif // platforms
2233
2234    wxLogSysError(_("Failed to modify file times for '%s'"),
2235                  GetFullPath().c_str());
2236
2237    return false;
2238}
2239
2240bool wxFileName::Touch()
2241{
2242#if defined(__UNIX_LIKE__)
2243    // under Unix touching file is simple: just pass NULL to utime()
2244    if ( utime(GetFullPath().fn_str(), NULL) == 0 )
2245    {
2246        return true;
2247    }
2248
2249    wxLogSysError(_("Failed to touch the file '%s'"), GetFullPath().c_str());
2250
2251    return false;
2252#else // other platform
2253    wxDateTime dtNow = wxDateTime::Now();
2254
2255    return SetTimes(&dtNow, &dtNow, NULL /* don't change create time */);
2256#endif // platforms
2257}
2258
2259#ifdef wxNEED_WX_UNISTD_H
2260
2261static int wxStat( const char *file_name, wxStructStat *buf )
2262{
2263    return stat( file_name , buf );
2264}
2265
2266#endif
2267
2268bool wxFileName::GetTimes(wxDateTime *dtAccess,
2269                          wxDateTime *dtMod,
2270                          wxDateTime *dtCreate) const
2271{
2272#if defined(__WIN32__)
2273    // we must use different methods for the files and directories under
2274    // Windows as CreateFile(GENERIC_READ) doesn't work for the directories and
2275    // CreateFile(FILE_FLAG_BACKUP_SEMANTICS) works -- but only under NT and
2276    // not 9x
2277    bool ok;
2278    FILETIME ftAccess, ftCreate, ftWrite;
2279    if ( IsDir() )
2280    {
2281        // implemented in msw/dir.cpp
2282        extern bool wxGetDirectoryTimes(const wxString& dirname,
2283                                        FILETIME *, FILETIME *, FILETIME *);
2284
2285        // we should pass the path without the trailing separator to
2286        // wxGetDirectoryTimes()
2287        ok = wxGetDirectoryTimes(GetPath(wxPATH_GET_VOLUME),
2288                                 &ftAccess, &ftCreate, &ftWrite);
2289    }
2290    else // file
2291    {
2292        wxFileHandle fh(GetFullPath(), wxFileHandle::Read);
2293        if ( fh.IsOk() )
2294        {
2295            ok = ::GetFileTime(fh,
2296                               dtCreate ? &ftCreate : NULL,
2297                               dtAccess ? &ftAccess : NULL,
2298                               dtMod ? &ftWrite : NULL) != 0;
2299        }
2300        else
2301        {
2302            ok = false;
2303        }
2304    }
2305
2306    if ( ok )
2307    {
2308        if ( dtCreate )
2309            ConvertFileTimeToWx(dtCreate, ftCreate);
2310        if ( dtAccess )
2311            ConvertFileTimeToWx(dtAccess, ftAccess);
2312        if ( dtMod )
2313            ConvertFileTimeToWx(dtMod, ftWrite);
2314
2315        return true;
2316    }
2317#elif defined(__UNIX_LIKE__) || defined(__WXMAC__) || defined(__OS2__) || (defined(__DOS__) && defined(__WATCOMC__))
2318    // no need to test for IsDir() here
2319    wxStructStat stBuf;
2320    if ( wxStat( GetFullPath().fn_str(), &stBuf) == 0 )
2321    {
2322        if ( dtAccess )
2323            dtAccess->Set(stBuf.st_atime);
2324        if ( dtMod )
2325            dtMod->Set(stBuf.st_mtime);
2326        if ( dtCreate )
2327            dtCreate->Set(stBuf.st_ctime);
2328
2329        return true;
2330    }
2331#else // other platform
2332    wxUnusedVar(dtAccess);
2333    wxUnusedVar(dtMod);
2334    wxUnusedVar(dtCreate);
2335#endif // platforms
2336
2337    wxLogSysError(_("Failed to retrieve file times for '%s'"),
2338                  GetFullPath().c_str());
2339
2340    return false;
2341}
2342
2343#endif // wxUSE_DATETIME
2344
2345
2346// ----------------------------------------------------------------------------
2347// file size functions
2348// ----------------------------------------------------------------------------
2349
2350/* static */
2351wxULongLong wxFileName::GetSize(const wxString &filename)
2352{
2353    if (!wxFileExists(filename))
2354        return wxInvalidSize;
2355
2356#if defined(__WXPALMOS__)
2357    // TODO
2358    return wxInvalidSize;
2359#elif defined(__WIN32__)
2360    wxFileHandle f(filename, wxFileHandle::Read);
2361    if (!f.IsOk())
2362        return wxInvalidSize;
2363
2364    DWORD lpFileSizeHigh;
2365    DWORD ret = GetFileSize(f, &lpFileSizeHigh);
2366    if ( ret == INVALID_FILE_SIZE && ::GetLastError() != NO_ERROR )
2367        return wxInvalidSize;
2368
2369    return wxULongLong(lpFileSizeHigh, ret);
2370#else // ! __WIN32__
2371    wxStructStat st;
2372#ifndef wxNEED_WX_UNISTD_H
2373    if (wxStat( filename.fn_str() , &st) != 0)
2374#else
2375    if (wxStat( filename, &st) != 0)
2376#endif
2377        return wxInvalidSize;
2378    return wxULongLong(st.st_size);
2379#endif
2380}
2381
2382/* static */
2383wxString wxFileName::GetHumanReadableSize(const wxULongLong &bs,
2384                                          const wxString &nullsize,
2385                                          int precision)
2386{
2387    static const double KILOBYTESIZE = 1024.0;
2388    static const double MEGABYTESIZE = 1024.0*KILOBYTESIZE;
2389    static const double GIGABYTESIZE = 1024.0*MEGABYTESIZE;
2390    static const double TERABYTESIZE = 1024.0*GIGABYTESIZE;
2391
2392    if (bs == 0 || bs == wxInvalidSize)
2393        return nullsize;
2394
2395    double bytesize = bs.ToDouble();
2396    if (bytesize < KILOBYTESIZE)
2397        return wxString::Format(_("%s B"), bs.ToString().c_str());
2398    if (bytesize < MEGABYTESIZE)
2399        return wxString::Format(_("%.*f kB"), precision, bytesize/KILOBYTESIZE);
2400    if (bytesize < GIGABYTESIZE)
2401        return wxString::Format(_("%.*f MB"), precision, bytesize/MEGABYTESIZE);
2402    if (bytesize < TERABYTESIZE)
2403        return wxString::Format(_("%.*f GB"), precision, bytesize/GIGABYTESIZE);
2404
2405    return wxString::Format(_("%.*f TB"), precision, bytesize/TERABYTESIZE);
2406}
2407
2408wxULongLong wxFileName::GetSize() const
2409{
2410    return GetSize(GetFullPath());
2411}
2412
2413wxString wxFileName::GetHumanReadableSize(const wxString &failmsg, int precision) const
2414{
2415    return GetHumanReadableSize(GetSize(), failmsg, precision);
2416}
2417
2418
2419// ----------------------------------------------------------------------------
2420// Mac-specific functions
2421// ----------------------------------------------------------------------------
2422
2423#ifdef __WXMAC__
2424
2425const short kMacExtensionMaxLength = 16 ;
2426class MacDefaultExtensionRecord
2427{
2428public :
2429  MacDefaultExtensionRecord()
2430  {
2431    m_ext[0] = 0 ;
2432    m_type = m_creator = 0 ;
2433  }
2434  MacDefaultExtensionRecord( const MacDefaultExtensionRecord& from )
2435  {
2436    wxStrcpy( m_ext , from.m_ext ) ;
2437    m_type = from.m_type ;
2438    m_creator = from.m_creator ;
2439  }
2440  MacDefaultExtensionRecord( const wxChar * extension , OSType type , OSType creator )
2441  {
2442    wxStrncpy( m_ext , extension , kMacExtensionMaxLength ) ;
2443    m_ext[kMacExtensionMaxLength] = 0 ;
2444    m_type = type ;
2445    m_creator = creator ;
2446  }
2447  wxChar m_ext[kMacExtensionMaxLength] ;
2448  OSType m_type ;
2449  OSType m_creator ;
2450}  ;
2451
2452WX_DECLARE_OBJARRAY(MacDefaultExtensionRecord, MacDefaultExtensionArray) ;
2453
2454bool gMacDefaultExtensionsInited = false ;
2455
2456#include "wx/arrimpl.cpp"
2457
2458WX_DEFINE_EXPORTED_OBJARRAY(MacDefaultExtensionArray) ;
2459
2460MacDefaultExtensionArray gMacDefaultExtensions ;
2461
2462// load the default extensions
2463MacDefaultExtensionRecord gDefaults[] =
2464{
2465    MacDefaultExtensionRecord( wxT("txt") , 'TEXT' , 'ttxt' ) ,
2466    MacDefaultExtensionRecord( wxT("tif") , 'TIFF' , '****' ) ,
2467    MacDefaultExtensionRecord( wxT("jpg") , 'JPEG' , '****' ) ,
2468} ;
2469
2470static void MacEnsureDefaultExtensionsLoaded()
2471{
2472    if ( !gMacDefaultExtensionsInited )
2473    {
2474        // we could load the pc exchange prefs here too
2475        for ( size_t i = 0 ; i < WXSIZEOF( gDefaults ) ; ++i )
2476        {
2477            gMacDefaultExtensions.Add( gDefaults[i] ) ;
2478        }
2479        gMacDefaultExtensionsInited = true ;
2480    }
2481}
2482
2483bool wxFileName::MacSetTypeAndCreator( wxUint32 type , wxUint32 creator )
2484{
2485    FSRef fsRef ;
2486    FSCatalogInfo catInfo;
2487    FileInfo *finfo ;
2488
2489    if ( wxMacPathToFSRef( GetFullPath() , &fsRef ) == noErr )
2490    {
2491        if ( FSGetCatalogInfo (&fsRef, kFSCatInfoFinderInfo, &catInfo, NULL, NULL, NULL) == noErr )
2492        {
2493            finfo = (FileInfo*)&catInfo.finderInfo;
2494            finfo->fileType = type ;
2495            finfo->fileCreator = creator ;
2496            FSSetCatalogInfo( &fsRef, kFSCatInfoFinderInfo, &catInfo ) ;
2497            return true ;
2498        }
2499    }
2500    return false ;
2501}
2502
2503bool wxFileName::MacGetTypeAndCreator( wxUint32 *type , wxUint32 *creator )
2504{
2505    FSRef fsRef ;
2506    FSCatalogInfo catInfo;
2507    FileInfo *finfo ;
2508
2509    if ( wxMacPathToFSRef( GetFullPath() , &fsRef ) == noErr )
2510    {
2511        if ( FSGetCatalogInfo (&fsRef, kFSCatInfoFinderInfo, &catInfo, NULL, NULL, NULL) == noErr )
2512        {
2513            finfo = (FileInfo*)&catInfo.finderInfo;
2514            *type = finfo->fileType ;
2515            *creator = finfo->fileCreator ;
2516            return true ;
2517        }
2518    }
2519    return false ;
2520}
2521
2522bool wxFileName::MacSetDefaultTypeAndCreator()
2523{
2524    wxUint32 type , creator ;
2525    if ( wxFileName::MacFindDefaultTypeAndCreator(GetExt() , &type ,
2526      &creator ) )
2527    {
2528        return MacSetTypeAndCreator( type , creator ) ;
2529    }
2530    return false;
2531}
2532
2533bool wxFileName::MacFindDefaultTypeAndCreator( const wxString& ext , wxUint32 *type , wxUint32 *creator )
2534{
2535  MacEnsureDefaultExtensionsLoaded() ;
2536  wxString extl = ext.Lower() ;
2537  for( int i = gMacDefaultExtensions.Count() - 1 ; i >= 0 ; --i )
2538  {
2539    if ( gMacDefaultExtensions.Item(i).m_ext == extl )
2540    {
2541      *type = gMacDefaultExtensions.Item(i).m_type ;
2542      *creator = gMacDefaultExtensions.Item(i).m_creator ;
2543      return true ;
2544    }
2545  }
2546  return false ;
2547}
2548
2549void wxFileName::MacRegisterDefaultTypeAndCreator( const wxString& ext , wxUint32 type , wxUint32 creator )
2550{
2551  MacEnsureDefaultExtensionsLoaded() ;
2552  MacDefaultExtensionRecord rec ;
2553  rec.m_type = type ;
2554  rec.m_creator = creator ;
2555  wxStrncpy( rec.m_ext , ext.Lower().c_str() , kMacExtensionMaxLength ) ;
2556  gMacDefaultExtensions.Add( rec ) ;
2557}
2558#endif
2559