]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/filename.cpp
Reverted bogus fix for bogus bug report #13402.
[wxWidgets.git] / src / common / filename.cpp
index ff25ddc3774dcfcdc233c623259aef18f7b8d9b9..f90221af89b4a5343d35370890afe3b17871f37b 100644 (file)
@@ -6,7 +6,7 @@
 // Created:     28.12.2000
 // RCS-ID:      $Id$
 // Copyright:   (c) 2000 Robert Roebling
 // Created:     28.12.2000
 // RCS-ID:      $Id$
 // Copyright:   (c) 2000 Robert Roebling
-// Licence:     wxWindows license
+// Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
 /*
 /////////////////////////////////////////////////////////////////////////////
 
 /*
                 drive:\dir1\dir2\...\dirN\filename.ext where drive is a single
                 letter. "." and ".." as for Unix but no "~".
 
                 drive:\dir1\dir2\...\dirN\filename.ext where drive is a single
                 letter. "." and ".." as for Unix but no "~".
 
-                There are also UNC names of the form \\share\fullpath
+                There are also UNC names of the form \\share\fullpath and
+                MSW unique volume names of the form \\?\Volume{GUID}\fullpath.
+
+                The latter provide a uniform way to access a volume regardless of
+                its current mount point, i.e. you can change a volume's mount
+                point from D: to E:, or even remove it, and still be able to
+                access it through its unique volume name. More on the subject can
+                be found in MSDN's article "Naming a Volume" that is currently at
+                http://msdn.microsoft.com/en-us/library/aa365248(VS.85).aspx.
+
 
    wxPATH_MAC:  Mac OS 8/9 and Mac OS X under CodeWarrior 7 format, absolute file
                 names have the form
 
    wxPATH_MAC:  Mac OS 8/9 and Mac OS X under CodeWarrior 7 format, absolute file
                 names have the form
@@ -32,9 +41,9 @@
                 or just
                     filename
                 (although :filename works as well).
                 or just
                     filename
                 (although :filename works as well).
-                :::filename.ext is not yet supported. TODO.
                 Since the volume is just part of the file path, it is not
                 Since the volume is just part of the file path, it is not
-                treated like a separate entity as it is done under DOS.
+                treated like a separate entity as it is done under DOS and
+                VMS, it is just treated as another dir.
 
    wxPATH_VMS:  VMS native format, absolute file names have the form
                     <device>:[dir1.dir2.dir3]file.txt
 
    wxPATH_VMS:  VMS native format, absolute file names have the form
                     <device>:[dir1.dir2.dir3]file.txt
@@ -47,7 +56,7 @@
                 Note that VMS uses different separators unlike Unix:
                  : always after the device. If the path does not contain : than
                    the default (the device of the current directory) is assumed.
                 Note that VMS uses different separators unlike Unix:
                  : always after the device. If the path does not contain : than
                    the default (the device of the current directory) is assumed.
-                 [ start of directory specyfication
+                 [ start of directory specification
                  . separator between directory and subdirectory
                  ] between directory and file
  */
                  . separator between directory and subdirectory
                  ] between directory and file
  */
 // headers
 // ----------------------------------------------------------------------------
 
 // headers
 // ----------------------------------------------------------------------------
 
-#ifdef __GNUG__
-    #pragma implementation "filename.h"
-#endif
-
 // For compilers that support precompilation, includes "wx.h".
 #include "wx/wxprec.h"
 
 #ifdef __BORLANDC__
 // For compilers that support precompilation, includes "wx.h".
 #include "wx/wxprec.h"
 
 #ifdef __BORLANDC__
-  #pragma hdrstop
+#pragma hdrstop
 #endif
 
 #ifndef WX_PRECOMP
 #endif
 
 #ifndef WX_PRECOMP
+    #ifdef __WXMSW__
+        #include "wx/msw/wrapwin.h" // For GetShort/LongPathName
+    #endif
+    #include "wx/dynarray.h"
     #include "wx/intl.h"
     #include "wx/log.h"
     #include "wx/intl.h"
     #include "wx/log.h"
+    #include "wx/utils.h"
+    #include "wx/crt.h"
 #endif
 
 #include "wx/filename.h"
 #endif
 
 #include "wx/filename.h"
+#include "wx/private/filename.h"
 #include "wx/tokenzr.h"
 #include "wx/config.h"          // for wxExpandEnvVars
 #include "wx/tokenzr.h"
 #include "wx/config.h"          // for wxExpandEnvVars
-#include "wx/utils.h"
+#include "wx/dynlib.h"
+#include "wx/dir.h"
 
 
-#if wxUSE_DYNLIB_CLASS
-    #include "wx/dynlib.h"
+#if defined(__WIN32__) && defined(__MINGW32__)
+    #include "wx/msw/gccpriv.h"
 #endif
 
 #endif
 
-// For GetShort/LongPathName
-#ifdef __WIN32__
-    #include <windows.h>
+#ifdef __WXMSW__
+#include "wx/msw/private.h"
+#endif
 
 
-    #include "wx/msw/winundef.h"
+#if defined(__WXMAC__)
+  #include  "wx/osx/private.h"  // includes mac headers
 #endif
 
 // utime() is POSIX so should normally be available on all Unices
 #ifdef __UNIX_LIKE__
 #endif
 
 // utime() is POSIX so should normally be available on all Unices
 #ifdef __UNIX_LIKE__
-    #include <sys/types.h>
-    #include <utime.h>
-    #include <sys/stat.h>
-    #include <unistd.h>
+#include <sys/types.h>
+#include <utime.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+
+#ifdef __DJGPP__
+#include <unistd.h>
 #endif
 
 #ifdef __MWERKS__
 #endif
 
 #ifdef __MWERKS__
-    #include <stat.h>
-    #include <unistd.h>
-    #include <unix.h>
+#ifdef __MACH__
+#include <sys/types.h>
+#include <utime.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#else
+#include <stat.h>
+#include <unistd.h>
+#include <unix.h>
+#endif
 #endif
 
 #ifdef __WATCOMC__
 #endif
 
 #ifdef __WATCOMC__
-    #include <io.h>
-    #include <sys/utime.h>
-    #include <sys/stat.h>
+#include <io.h>
+#include <sys/utime.h>
+#include <sys/stat.h>
 #endif
 
 #ifdef __VISAGECPP__
 #endif
 
 #ifdef __VISAGECPP__
 #endif
 #endif
 
 #endif
 #endif
 
+#ifdef __EMX__
+#include <os2.h>
+#define MAX_PATH _MAX_PATH
+#endif
+
+
+#if wxUSE_LONGLONG
+extern const wxULongLong wxInvalidSize = (unsigned)-1;
+#endif // wxUSE_LONGLONG
+
+namespace
+{
+
 // ----------------------------------------------------------------------------
 // private classes
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 // private classes
 // ----------------------------------------------------------------------------
 class wxFileHandle
 {
 public:
 class wxFileHandle
 {
 public:
-    wxFileHandle(const wxString& filename)
+    enum OpenMode
+    {
+        ReadAttr,
+        WriteAttr
+    };
+
+    wxFileHandle(const wxString& filename, OpenMode mode, int flags = 0)
     {
     {
+        // be careful and use FILE_{READ,WRITE}_ATTRIBUTES here instead of the
+        // usual GENERIC_{READ,WRITE} as we don't want the file access time to
+        // be changed when we open it because this class is used for setting
+        // access time (see #10567)
         m_hFile = ::CreateFile
                     (
         m_hFile = ::CreateFile
                     (
-                     filename,          // name
-                     GENERIC_READ,      // access mask
-                     0,                 // no sharing
-                     NULL,              // no secutity attr
-                     OPEN_EXISTING,     // creation disposition
-                     0,                 // no flags
-                     NULL               // no template file
+                     filename.t_str(),             // name
+                     mode == ReadAttr ? FILE_READ_ATTRIBUTES    // access mask
+                                      : FILE_WRITE_ATTRIBUTES,
+                     FILE_SHARE_READ |              // sharing mode
+                     FILE_SHARE_WRITE,              // (allow everything)
+                     NULL,                          // no secutity attr
+                     OPEN_EXISTING,                 // creation disposition
+                     flags,                         // flags
+                     NULL                           // no template file
                     );
 
         if ( m_hFile == INVALID_HANDLE_VALUE )
         {
                     );
 
         if ( m_hFile == INVALID_HANDLE_VALUE )
         {
-            wxLogSysError(_("Failed to open '%s' for reading"),
-                          filename.c_str());
+            if ( mode == ReadAttr )
+            {
+                wxLogSysError(_("Failed to open '%s' for reading"),
+                              filename.c_str());
+            }
+            else
+            {
+                wxLogSysError(_("Failed to open '%s' for writing"),
+                              filename.c_str());
+            }
         }
     }
 
         }
     }
 
@@ -160,7 +218,7 @@ public:
         }
     }
 
         }
     }
 
-    // return TRUE only if the file could be opened successfully
+    // return true only if the file could be opened successfully
     bool IsOk() const { return m_hFile != INVALID_HANDLE_VALUE; }
 
     // get the handle
     bool IsOk() const { return m_hFile != INVALID_HANDLE_VALUE; }
 
     // get the handle
@@ -176,40 +234,119 @@ private:
 // private functions
 // ----------------------------------------------------------------------------
 
 // private functions
 // ----------------------------------------------------------------------------
 
-#if defined(__WIN32__) && !defined(__WXMICROWIN__)
+#if wxUSE_DATETIME && defined(__WIN32__) && !defined(__WXMICROWIN__)
 
 // convert between wxDateTime and FILETIME which is a 64-bit value representing
 // the number of 100-nanosecond intervals since January 1, 1601.
 
 
 // convert between wxDateTime and FILETIME which is a 64-bit value representing
 // the number of 100-nanosecond intervals since January 1, 1601.
 
-// the number of milliseconds between the Unix Epoch (January 1, 1970) and the
-// FILETIME reference point (January 1, 1601)
-static const wxLongLong FILETIME_EPOCH_OFFSET = wxLongLong(0xa97, 0x30b66800);
-
 static void ConvertFileTimeToWx(wxDateTime *dt, const FILETIME &ft)
 {
 static void ConvertFileTimeToWx(wxDateTime *dt, const FILETIME &ft)
 {
-    wxLongLong ll(ft.dwHighDateTime, ft.dwLowDateTime);
-
-    // convert 100ns to ms
-    ll /= 10000;
+    FILETIME ftcopy = ft;
+    FILETIME ftLocal;
+    if ( !::FileTimeToLocalFileTime(&ftcopy, &ftLocal) )
+    {
+        wxLogLastError(wxT("FileTimeToLocalFileTime"));
+    }
 
 
-    // move it to our Epoch
-    ll -= FILETIME_EPOCH_OFFSET;
+    SYSTEMTIME st;
+    if ( !::FileTimeToSystemTime(&ftLocal, &st) )
+    {
+        wxLogLastError(wxT("FileTimeToSystemTime"));
+    }
 
 
-    *dt = wxDateTime(ll);
+    dt->Set(st.wDay, wxDateTime::Month(st.wMonth - 1), st.wYear,
+            st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
 }
 
 static void ConvertWxToFileTime(FILETIME *ft, const wxDateTime& dt)
 {
 }
 
 static void ConvertWxToFileTime(FILETIME *ft, const wxDateTime& dt)
 {
-    // do the reverse of ConvertFileTimeToWx()
-    wxLongLong ll = dt.GetValue();
-    ll *= 10000;
-    ll += FILETIME_EPOCH_OFFSET;
+    SYSTEMTIME st;
+    st.wDay = dt.GetDay();
+    st.wMonth = (WORD)(dt.GetMonth() + 1);
+    st.wYear = (WORD)dt.GetYear();
+    st.wHour = dt.GetHour();
+    st.wMinute = dt.GetMinute();
+    st.wSecond = dt.GetSecond();
+    st.wMilliseconds = dt.GetMillisecond();
+
+    FILETIME ftLocal;
+    if ( !::SystemTimeToFileTime(&st, &ftLocal) )
+    {
+        wxLogLastError(wxT("SystemTimeToFileTime"));
+    }
+
+    if ( !::LocalFileTimeToFileTime(&ftLocal, ft) )
+    {
+        wxLogLastError(wxT("LocalFileTimeToFileTime"));
+    }
+}
+
+#endif // wxUSE_DATETIME && __WIN32__
+
+// return a string with the volume par
+static wxString wxGetVolumeString(const wxString& volume, wxPathFormat format)
+{
+    wxString path;
+
+    if ( !volume.empty() )
+    {
+        format = wxFileName::GetFormat(format);
+
+        // Special Windows UNC paths hack, part 2: undo what we did in
+        // SplitPath() and make an UNC path if we have a drive which is not a
+        // single letter (hopefully the network shares can't be one letter only
+        // although I didn't find any authoritative docs on this)
+        if ( format == wxPATH_DOS && volume.length() > 1 )
+        {
+            // We also have to check for Windows unique volume names here and
+            // return it with '\\?\' prepended to it
+            if ( wxFileName::IsMSWUniqueVolumeNamePath("\\\\?\\" + volume + "\\",
+                                                       format) )
+            {
+                path << "\\\\?\\" << volume;
+            }
+            else
+            {
+                // it must be a UNC path
+                path << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_DOS << volume;
+            }
+        }
+        else if  ( format == wxPATH_DOS || format == wxPATH_VMS )
+        {
+            path << volume << wxFileName::GetVolumeSeparator(format);
+        }
+        // else ignore
+    }
+
+    return path;
+}
+
+// return true if the character is a DOS path separator i.e. either a slash or
+// a backslash
+inline bool IsDOSPathSep(wxUniChar ch)
+{
+    return ch == wxFILE_SEP_PATH_DOS || ch == wxFILE_SEP_PATH_UNIX;
+}
 
 
-    ft->dwHighDateTime = ll.GetHi();
-    ft->dwLowDateTime = ll.GetLo();
+// return true if the format used is the DOS/Windows one and the string looks
+// like a UNC path
+static bool IsUNCPath(const wxString& path, wxPathFormat format)
+{
+    return format == wxPATH_DOS &&
+                path.length() >= 4 && // "\\a" can't be a UNC path
+                    IsDOSPathSep(path[0u]) &&
+                        IsDOSPathSep(path[1u]) &&
+                            !IsDOSPathSep(path[2u]);
 }
 
 }
 
-#endif // __WIN32__
+// ----------------------------------------------------------------------------
+// private constants
+// ----------------------------------------------------------------------------
+
+// length of \\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\ string
+static const size_t wxMSWUniqueVolumePrefixLength = 49;
+
+} // anonymous namespace
 
 // ============================================================================
 // implementation
 
 // ============================================================================
 // implementation
@@ -225,40 +362,151 @@ void wxFileName::Assign( const wxFileName &filepath )
     m_dirs = filepath.GetDirs();
     m_name = filepath.GetName();
     m_ext = filepath.GetExt();
     m_dirs = filepath.GetDirs();
     m_name = filepath.GetName();
     m_ext = filepath.GetExt();
+    m_relative = filepath.m_relative;
+    m_hasExt = filepath.m_hasExt;
 }
 
 void wxFileName::Assign(const wxString& volume,
                         const wxString& path,
                         const wxString& name,
                         const wxString& ext,
 }
 
 void wxFileName::Assign(const wxString& volume,
                         const wxString& path,
                         const wxString& name,
                         const wxString& ext,
-                        wxPathFormat format )
+                        bool hasExt,
+                        wxPathFormat format)
 {
 {
-    wxStringTokenizer tn(path, GetPathSeparators(format));
+    // we should ignore paths which look like UNC shares because we already
+    // have the volume here and the UNC notation (\\server\path) is only valid
+    // for paths which don't start with a volume, so prevent SetPath() from
+    // recognizing "\\foo\bar" in "c:\\foo\bar" as an UNC path
+    //
+    // note also that this is a rather ugly way to do what we want (passing
+    // some kind of flag telling to ignore UNC paths to SetPath() would be
+    // better) but this is the safest thing to do to avoid breaking backwards
+    // compatibility in 2.8
+    if ( IsUNCPath(path, format) )
+    {
+        // remove one of the 2 leading backslashes to ensure that it's not
+        // recognized as an UNC path by SetPath()
+        wxString pathNonUNC(path, 1, wxString::npos);
+        SetPath(pathNonUNC, format);
+    }
+    else // no UNC complications
+    {
+        SetPath(path, format);
+    }
+
+    m_volume = volume;
+    m_ext = ext;
+    m_name = name;
+
+    m_hasExt = hasExt;
+}
 
 
+void wxFileName::SetPath( const wxString& pathOrig, wxPathFormat format )
+{
     m_dirs.Clear();
     m_dirs.Clear();
+
+    if ( pathOrig.empty() )
+    {
+        // no path at all
+        m_relative = true;
+
+        return;
+    }
+
+    format = GetFormat( format );
+
+    // 0) deal with possible volume part first
+    wxString volume,
+             path;
+    SplitVolume(pathOrig, &volume, &path, format);
+    if ( !volume.empty() )
+    {
+        m_relative = false;
+
+        SetVolume(volume);
+    }
+
+    // 1) Determine if the path is relative or absolute.
+
+    if ( path.empty() )
+    {
+        // we had only the volume
+        return;
+    }
+
+    wxChar leadingChar = path[0u];
+
+    switch (format)
+    {
+        case wxPATH_MAC:
+            m_relative = leadingChar == wxT(':');
+
+            // We then remove a leading ":". The reason is in our
+            // storage form for relative paths:
+            // ":dir:file.txt" actually means "./dir/file.txt" in
+            // DOS notation and should get stored as
+            // (relative) (dir) (file.txt)
+            // "::dir:file.txt" actually means "../dir/file.txt"
+            // stored as (relative) (..) (dir) (file.txt)
+            // This is important only for the Mac as an empty dir
+            // actually means <UP>, whereas under DOS, double
+            // slashes can be ignored: "\\\\" is the same as "\\".
+            if (m_relative)
+                path.erase( 0, 1 );
+            break;
+
+        case wxPATH_VMS:
+            // TODO: what is the relative path format here?
+            m_relative = false;
+            break;
+
+        default:
+            wxFAIL_MSG( wxT("Unknown path format") );
+            // !! Fall through !!
+
+        case wxPATH_UNIX:
+            m_relative = leadingChar != wxT('/');
+            break;
+
+        case wxPATH_DOS:
+            m_relative = !IsPathSeparator(leadingChar, format);
+            break;
+
+    }
+
+    // 2) Break up the path into its members. If the original path
+    //    was just "/" or "\\", m_dirs will be empty. We know from
+    //    the m_relative field, if this means "nothing" or "root dir".
+
+    wxStringTokenizer tn( path, GetPathSeparators(format) );
+
     while ( tn.HasMoreTokens() )
     {
         wxString token = tn.GetNextToken();
 
     while ( tn.HasMoreTokens() )
     {
         wxString token = tn.GetNextToken();
 
-        // if the path starts with a slash, we do need the first empty dir
-        // entry to be able to tell later that it was an absolute path, but
-        // otherwise ignore the double slashes
-        if ( m_dirs.IsEmpty() || !token.IsEmpty() )
-            m_dirs.Add( token );
+        // Remove empty token under DOS and Unix, interpret them
+        // as .. under Mac.
+        if (token.empty())
+        {
+            if (format == wxPATH_MAC)
+                m_dirs.Add( wxT("..") );
+            // else ignore
+        }
+        else
+        {
+           m_dirs.Add( token );
+        }
     }
     }
-
-    m_volume = volume;
-    m_ext = ext;
-    m_name = name;
 }
 
 void wxFileName::Assign(const wxString& fullpath,
                         wxPathFormat format)
 {
     wxString volume, path, name, ext;
 }
 
 void wxFileName::Assign(const wxString& fullpath,
                         wxPathFormat format)
 {
     wxString volume, path, name, ext;
-    SplitPath(fullpath, &volume, &path, &name, &ext, format);
+    bool hasExt;
+    SplitPath(fullpath, &volume, &path, &name, &ext, &hasExt, format);
 
 
-    Assign(volume, path, name, ext, format);
+    Assign(volume, path, name, ext, hasExt, format);
 }
 
 void wxFileName::Assign(const wxString& fullpathOrig,
 }
 
 void wxFileName::Assign(const wxString& fullpathOrig,
@@ -268,39 +516,48 @@ void wxFileName::Assign(const wxString& fullpathOrig,
     // always recognize fullpath as directory, even if it doesn't end with a
     // slash
     wxString fullpath = fullpathOrig;
     // always recognize fullpath as directory, even if it doesn't end with a
     // slash
     wxString fullpath = fullpathOrig;
-    if ( !wxEndsWithPathSeparator(fullpath) )
+    if ( !fullpath.empty() && !wxEndsWithPathSeparator(fullpath) )
     {
     {
-        fullpath += GetPathSeparators(format)[0u];
+        fullpath += GetPathSeparator(format);
     }
 
     wxString volume, path, name, ext;
     }
 
     wxString volume, path, name, ext;
+    bool hasExt;
 
 
-    // do some consistency checks in debug mode: the name should be really just
-    // the filename and the path should be realyl just a path
-#ifdef __WXDEBUG__
-    wxString pathDummy, nameDummy, extDummy;
+    // do some consistency checks: the name should be really just the filename
+    // and the path should be really just a path
+    wxString volDummy, pathDummy, nameDummy, extDummy;
 
 
-    SplitPath(fullname, &pathDummy, &name, &ext, format);
+    SplitPath(fullname, &volDummy, &pathDummy, &name, &ext, &hasExt, format);
 
 
-    wxASSERT_MSG( pathDummy.empty(),
-                  _T("the file name shouldn't contain the path") );
+    wxASSERT_MSG( volDummy.empty() && pathDummy.empty(),
+                  wxT("the file name shouldn't contain the path") );
 
     SplitPath(fullpath, &volume, &path, &nameDummy, &extDummy, format);
 
 
     SplitPath(fullpath, &volume, &path, &nameDummy, &extDummy, format);
 
-    wxASSERT_MSG( nameDummy.empty() && extDummy.empty(),
-                  _T("the path shouldn't contain file name nor extension") );
+#ifndef __VMS
+   // This test makes no sense on an OpenVMS system.
+   wxASSERT_MSG( nameDummy.empty() && extDummy.empty(),
+                  wxT("the path shouldn't contain file name nor extension") );
+#endif
+    Assign(volume, path, name, ext, hasExt, format);
+}
 
 
-#else // !__WXDEBUG__
-    SplitPath(fullname, NULL /* no path */, &name, &ext, format);
-    SplitPath(fullpath, &volume, &path, NULL, NULL, format);
-#endif // __WXDEBUG__/!__WXDEBUG__
+void wxFileName::Assign(const wxString& pathOrig,
+                        const wxString& name,
+                        const wxString& ext,
+                        wxPathFormat format)
+{
+    wxString volume,
+             path;
+    SplitVolume(pathOrig, &volume, &path, format);
 
     Assign(volume, path, name, ext, format);
 }
 
 void wxFileName::AssignDir(const wxString& dir, wxPathFormat format)
 {
 
     Assign(volume, path, name, ext, format);
 }
 
 void wxFileName::AssignDir(const wxString& dir, wxPathFormat format)
 {
-    Assign(dir, _T(""), format);
+    Assign(dir, wxEmptyString, format);
 }
 
 void wxFileName::Clear()
 }
 
 void wxFileName::Clear()
@@ -310,19 +567,25 @@ void wxFileName::Clear()
     m_volume =
     m_name =
     m_ext = wxEmptyString;
     m_volume =
     m_name =
     m_ext = wxEmptyString;
+
+    // we don't have any absolute path for now
+    m_relative = true;
+
+    // nor any extension
+    m_hasExt = false;
 }
 
 /* static */
 }
 
 /* static */
-wxFileName wxFileName::FileName(const wxString& file)
+wxFileName wxFileName::FileName(const wxString& file, wxPathFormat format)
 {
 {
-    return wxFileName(file);
+    return wxFileName(file, format);
 }
 
 /* static */
 }
 
 /* static */
-wxFileName wxFileName::DirName(const wxString& dir)
+wxFileName wxFileName::DirName(const wxString& dir, wxPathFormat format)
 {
     wxFileName fn;
 {
     wxFileName fn;
-    fn.AssignDir(dir);
+    fn.AssignDir(dir, format);
     return fn;
 }
 
     return fn;
 }
 
@@ -330,24 +593,94 @@ wxFileName wxFileName::DirName(const wxString& dir)
 // existence tests
 // ----------------------------------------------------------------------------
 
 // existence tests
 // ----------------------------------------------------------------------------
 
-bool wxFileName::FileExists()
+bool wxFileName::FileExists() const
 {
     return wxFileName::FileExists( GetFullPath() );
 }
 
 {
     return wxFileName::FileExists( GetFullPath() );
 }
 
-bool wxFileName::FileExists( const wxString &file )
+/* static */
+bool wxFileName::FileExists( const wxString &filePath )
 {
 {
-    return ::wxFileExists( file );
+#if defined(__WIN32__) && !defined(__WXMICROWIN__)
+    // we must use GetFileAttributes() instead of the ANSI C functions because
+    // it can cope with network (UNC) paths unlike them
+    DWORD ret = ::GetFileAttributes(filePath.t_str());
+
+    return (ret != INVALID_FILE_ATTRIBUTES) && !(ret & FILE_ATTRIBUTE_DIRECTORY);
+#else // !__WIN32__
+    #ifndef S_ISREG
+        #define S_ISREG(mode) ((mode) & S_IFREG)
+    #endif
+    wxStructStat st;
+
+    return (wxStat( filePath, &st) == 0 && S_ISREG(st.st_mode))
+#ifdef __OS2__
+      || (errno == EACCES) // if access is denied something with that name
+                            // exists and is opened in exclusive mode.
+#endif
+      ;
+#endif // __WIN32__/!__WIN32__
 }
 
 }
 
-bool wxFileName::DirExists()
+bool wxFileName::DirExists() const
 {
 {
-    return wxFileName::DirExists( GetFullPath() );
+    return wxFileName::DirExists( GetPath() );
 }
 
 }
 
-bool wxFileName::DirExists( const wxString &dir )
+/* static */
+bool wxFileName::DirExists( const wxString &dirPath )
 {
 {
-    return ::wxDirExists( dir );
+    wxString strPath(dirPath);
+
+#if defined(__WINDOWS__) || defined(__OS2__)
+    // Windows fails to find directory named "c:\dir\" even if "c:\dir" exists,
+    // so remove all trailing backslashes from the path - but don't do this for
+    // the paths "d:\" (which are different from "d:"), for just "\" or for
+    // windows unique volume names ("\\?\Volume{GUID}\")
+    while ( wxEndsWithPathSeparator(strPath) )
+    {
+        size_t len = strPath.length();
+        if ( len == 1 || (len == 3 && strPath[len - 2] == wxT(':')) ||
+            (len == wxMSWUniqueVolumePrefixLength &&
+             wxFileName::IsMSWUniqueVolumeNamePath(strPath)))
+        {
+            break;
+        }
+
+        strPath.Truncate(len - 1);
+    }
+#endif // __WINDOWS__
+
+#ifdef __OS2__
+    // OS/2 can't handle "d:", it wants either "d:\" or "d:."
+    if (strPath.length() == 2 && strPath[1u] == wxT(':'))
+        strPath << wxT('.');
+#endif
+
+#if defined(__WIN32__) && !defined(__WXMICROWIN__)
+    // stat() can't cope with network paths
+    DWORD ret = ::GetFileAttributes(strPath.t_str());
+
+    return (ret != INVALID_FILE_ATTRIBUTES) && (ret & FILE_ATTRIBUTE_DIRECTORY);
+#elif defined(__OS2__)
+    FILESTATUS3 Info = {{0}};
+    APIRET rc = ::DosQueryPathInfo((PSZ)(WXSTRINGCAST strPath), FIL_STANDARD,
+                                   (void*) &Info, sizeof(FILESTATUS3));
+
+    return ((rc == NO_ERROR) && (Info.attrFile & FILE_DIRECTORY)) ||
+      (rc == ERROR_SHARING_VIOLATION);
+    // If we got a sharing violation, there must be something with this name.
+#else // !__WIN32__
+
+    wxStructStat st;
+#ifndef __VISAGECPP__
+    return wxStat(strPath, &st) == 0 && ((st.st_mode & S_IFMT) == S_IFDIR);
+#else
+    // S_IFMT not supported in VA compilers.. st_mode is a 2byte value only
+    return wxStat(strPath, &st) == 0 && (st.st_mode == S_IFDIR);
+#endif
+
+#endif // __WIN32__/!__WIN32__
 }
 
 // ----------------------------------------------------------------------------
 }
 
 // ----------------------------------------------------------------------------
@@ -383,9 +716,9 @@ wxString wxFileName::GetCwd(const wxString& volume)
     return cwd;
 }
 
     return cwd;
 }
 
-bool wxFileName::SetCwd()
+bool wxFileName::SetCwd() const
 {
 {
-    return wxFileName::SetCwd( GetFullPath() );
+    return wxFileName::SetCwd( GetPath() );
 }
 
 bool wxFileName::SetCwd( const wxString &cwd )
 }
 
 bool wxFileName::SetCwd( const wxString &cwd )
@@ -403,92 +736,144 @@ wxString wxFileName::GetHomeDir()
     return ::wxGetHomeDir();
 }
 
     return ::wxGetHomeDir();
 }
 
-void wxFileName::AssignTempFileName( const wxString& prefix )
+
+// ----------------------------------------------------------------------------
+// CreateTempFileName
+// ----------------------------------------------------------------------------
+
+#if wxUSE_FILE || wxUSE_FFILE
+
+
+#if !defined wx_fdopen && defined HAVE_FDOPEN
+    #define wx_fdopen fdopen
+#endif
+
+// NB: GetTempFileName() under Windows creates the file, so using
+//     O_EXCL there would fail
+#ifdef __WINDOWS__
+    #define wxOPEN_EXCL 0
+#else
+    #define wxOPEN_EXCL O_EXCL
+#endif
+
+
+#ifdef wxOpenOSFHandle
+#define WX_HAVE_DELETE_ON_CLOSE
+// On Windows create a file with the FILE_FLAGS_DELETE_ON_CLOSE flags.
+//
+static int wxOpenWithDeleteOnClose(const wxString& filename)
 {
 {
-    wxString tempname = CreateTempFileName(prefix);
-    if ( tempname.empty() )
-    {
-        // error, failed to get temp file name
-        Clear();
-    }
-    else // ok
-    {
-        Assign(tempname);
-    }
+    DWORD access = GENERIC_READ | GENERIC_WRITE;
+
+    DWORD disposition = OPEN_ALWAYS;
+
+    DWORD attributes = FILE_ATTRIBUTE_TEMPORARY |
+                       FILE_FLAG_DELETE_ON_CLOSE;
+
+    HANDLE h = ::CreateFile(filename.fn_str(), access, 0, NULL,
+                            disposition, attributes, NULL);
+
+    return wxOpenOSFHandle(h, wxO_BINARY);
 }
 }
+#endif // wxOpenOSFHandle
 
 
-/* static */
-wxString wxFileName::CreateTempFileName(const wxString& prefix)
+
+// Helper to open the file
+//
+static int wxTempOpen(const wxString& path, bool *deleteOnClose)
 {
 {
-    wxString path, dir, name;
+#ifdef WX_HAVE_DELETE_ON_CLOSE
+    if (*deleteOnClose)
+        return wxOpenWithDeleteOnClose(path);
+#endif
 
 
-    // use the directory specified by the prefix
-    SplitPath(prefix, &dir, &name, NULL /* extension */);
+    *deleteOnClose = false;
 
 
-#if defined(__WINDOWS__) && !defined(__WXMICROWIN__)
+    return wxOpen(path, wxO_BINARY | O_RDWR | O_CREAT | wxOPEN_EXCL, 0600);
+}
 
 
-#ifdef __WIN32__
-    if ( dir.empty() )
-    {
-        if ( !::GetTempPath(MAX_PATH, wxStringBuffer(dir, MAX_PATH + 1)) )
-        {
-            wxLogLastError(_T("GetTempPath"));
-        }
 
 
-        if ( dir.empty() )
-        {
-            // GetTempFileName() fails if we pass it an empty string
-            dir = _T('.');
-        }
-    }
+#if wxUSE_FFILE
+// Helper to open the file and attach it to the wxFFile
+//
+static bool wxTempOpen(wxFFile *file, const wxString& path, bool *deleteOnClose)
+{
+#ifndef wx_fdopen
+    *deleteOnClose = false;
+    return file->Open(path, wxT("w+b"));
+#else // wx_fdopen
+    int fd = wxTempOpen(path, deleteOnClose);
+    if (fd == -1)
+        return false;
+    file->Attach(wx_fdopen(fd, "w+b"), path);
+    return file->IsOpened();
+#endif // wx_fdopen
+}
+#endif // wxUSE_FFILE
 
 
-    if ( !::GetTempFileName(dir, name, 0, wxStringBuffer(path, MAX_PATH + 1)) )
+
+#if !wxUSE_FILE
+    #define WXFILEARGS(x, y) y
+#elif !wxUSE_FFILE
+    #define WXFILEARGS(x, y) x
+#else
+    #define WXFILEARGS(x, y) x, y
+#endif
+
+
+// Implementation of wxFileName::CreateTempFileName().
+//
+static wxString wxCreateTempImpl(
+        const wxString& prefix,
+        WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp),
+        bool *deleteOnClose = NULL)
+{
+#if wxUSE_FILE && wxUSE_FFILE
+    wxASSERT(fileTemp == NULL || ffileTemp == NULL);
+#endif
+    wxString path, dir, name;
+    bool wantDeleteOnClose = false;
+
+    if (deleteOnClose)
+    {
+        // set the result to false initially
+        wantDeleteOnClose = *deleteOnClose;
+        *deleteOnClose = false;
+    }
+    else
     {
     {
-        wxLogLastError(_T("GetTempFileName"));
+        // easier if it alwasys points to something
+        deleteOnClose = &wantDeleteOnClose;
+    }
 
 
-        path.clear();
+    // use the directory specified by the prefix
+    wxFileName::SplitPath(prefix, &dir, &name, NULL /* extension */);
+
+    if (dir.empty())
+    {
+        dir = wxFileName::GetTempDir();
     }
     }
-#else // Win16
-    if ( !::GetTempFileName(NULL, prefix, 0, wxStringBuffer(path, 1025)) )
+
+#if defined(__WXWINCE__)
+    path = dir + wxT("\\") + name;
+    int i = 1;
+    while (wxFileName::FileExists(path))
     {
     {
-        path.clear();
+        path = dir + wxT("\\") + name ;
+        path << i;
+        i ++;
     }
     }
-#endif // Win32/16
 
 
-#elif defined(__WXPM__)
-    // for now just create a file
-    //
-    // future enhancements can be to set some extended attributes for file
-    // systems OS/2 supports that have them (HPFS, FAT32) and security
-    // (HPFS386)
-    static const wxChar *szMktempSuffix = wxT("XXX");
-    path << dir << _T('/') << name << szMktempSuffix;
-
-    // Temporarily remove - MN
-    #ifndef __WATCOMC__
-        ::DosCreateDir(wxStringBuffer(path, MAX_PATH), NULL);
-    #endif
-    
-#else // !Windows, !OS/2
-    if ( dir.empty() )
+#elif defined(__WINDOWS__) && !defined(__WXMICROWIN__)
+    if (!::GetTempFileName(dir.t_str(), name.t_str(), 0,
+                           wxStringBuffer(path, MAX_PATH + 1)))
     {
     {
-        dir = wxGetenv(_T("TMP"));
-        if ( path.empty() )
-        {
-            dir = wxGetenv(_T("TEMP"));
-        }
+        wxLogLastError(wxT("GetTempFileName"));
 
 
-        if ( dir.empty() )
-        {
-            // default
-            #ifdef __DOS__
-            dir = _T(".");
-            #else
-            dir = _T("/tmp");
-            #endif
-        }
+        path.clear();
     }
 
     }
 
+#else // !Windows
     path = dir;
 
     if ( !wxEndsWithPathSeparator(dir) &&
     path = dir;
 
     if ( !wxEndsWithPathSeparator(dir) &&
@@ -501,30 +886,67 @@ wxString wxFileName::CreateTempFileName(const wxString& prefix)
 
 #if defined(HAVE_MKSTEMP)
     // scratch space for mkstemp()
 
 #if defined(HAVE_MKSTEMP)
     // scratch space for mkstemp()
-    path += _T("XXXXXX");
+    path += wxT("XXXXXX");
 
 
-    // can use the cast here because the length doesn't change and the string
-    // is not shared
-    if ( mkstemp((char *)path.mb_str()) == -1 )
+    // we need to copy the path to the buffer in which mkstemp() can modify it
+    wxCharBuffer buf(path.fn_str());
+
+    // cast is safe because the string length doesn't change
+    int fdTemp = mkstemp( (char*)(const char*) buf );
+    if ( fdTemp == -1 )
     {
         // this might be not necessary as mkstemp() on most systems should have
         // already done it but it doesn't hurt neither...
         path.clear();
     }
     {
         // this might be not necessary as mkstemp() on most systems should have
         // already done it but it doesn't hurt neither...
         path.clear();
     }
-    //else: file already created
+    else // mkstemp() succeeded
+    {
+        path = wxConvFile.cMB2WX( (const char*) buf );
+
+    #if wxUSE_FILE
+        // avoid leaking the fd
+        if ( fileTemp )
+        {
+            fileTemp->Attach(fdTemp);
+        }
+        else
+    #endif
+
+    #if wxUSE_FFILE
+        if ( ffileTemp )
+        {
+        #ifdef wx_fdopen
+            ffileTemp->Attach(wx_fdopen(fdTemp, "r+b"), path);
+        #else
+            ffileTemp->Open(path, wxT("r+b"));
+            close(fdTemp);
+        #endif
+        }
+        else
+    #endif
+
+        {
+            close(fdTemp);
+        }
+    }
 #else // !HAVE_MKSTEMP
 
 #ifdef HAVE_MKTEMP
     // same as above
 #else // !HAVE_MKSTEMP
 
 #ifdef HAVE_MKTEMP
     // same as above
-    path += _T("XXXXXX");
+    path += wxT("XXXXXX");
 
 
-    if ( !mktemp((char *)path.mb_str()) )
+    wxCharBuffer buf = wxConvFile.cWX2MB( path );
+    if ( !mktemp( (char*)(const char*) buf ) )
     {
         path.clear();
     }
     {
         path.clear();
     }
+    else
+    {
+        path = wxConvFile.cMB2WX( (const char*) buf );
+    }
 #else // !HAVE_MKTEMP (includes __DOS__)
     // generate the unique file name ourselves
 #else // !HAVE_MKTEMP (includes __DOS__)
     // generate the unique file name ourselves
-    #ifndef __DOS__
+    #if !defined(__DOS__) && (!defined(__MWERKS__) || defined(__DARWIN__) )
     path << (unsigned int)getpid();
     #endif
 
     path << (unsigned int)getpid();
     #endif
 
@@ -534,8 +956,8 @@ wxString wxFileName::CreateTempFileName(const wxString& prefix)
     for ( size_t n = 0; n < numTries; n++ )
     {
         // 3 hex digits is enough for numTries == 1000 < 4096
     for ( size_t n = 0; n < numTries; n++ )
     {
         // 3 hex digits is enough for numTries == 1000 < 4096
-        pathTry = path + wxString::Format(_T("%.03x"), n);
-        if ( !wxFile::Exists(pathTry) )
+        pathTry = path + wxString::Format(wxT("%.03x"), (unsigned int) n);
+        if ( !wxFileName::FileExists(pathTry) )
         {
             break;
         }
         {
             break;
         }
@@ -546,23 +968,6 @@ wxString wxFileName::CreateTempFileName(const wxString& prefix)
     path = pathTry;
 #endif // HAVE_MKTEMP/!HAVE_MKTEMP
 
     path = pathTry;
 #endif // HAVE_MKTEMP/!HAVE_MKTEMP
 
-    if ( !path.empty() )
-    {
-        // create the file - of course, there is a race condition here, this is
-        // why we always prefer using mkstemp()...
-        wxFile file;
-        if ( !file.Open(path, wxFile::write_excl, wxS_IRUSR | wxS_IWUSR) )
-        {
-            // FIXME: If !ok here should we loop and try again with another
-            //        file name?  That is the standard recourse if open(O_EXCL)
-            //        fails, though of course it should be protected against
-            //        possible infinite looping too.
-
-            wxLogError(_("Failed to open temporary file."));
-
-            path.clear();
-        }
-    }
 #endif // HAVE_MKSTEMP/!HAVE_MKSTEMP
 
 #endif // Windows/!Windows
 #endif // HAVE_MKSTEMP/!HAVE_MKSTEMP
 
 #endif // Windows/!Windows
@@ -571,75 +976,388 @@ wxString wxFileName::CreateTempFileName(const wxString& prefix)
     {
         wxLogSysError(_("Failed to create a temporary file name"));
     }
     {
         wxLogSysError(_("Failed to create a temporary file name"));
     }
+    else
+    {
+        bool ok = true;
+
+        // open the file - of course, there is a race condition here, this is
+        // why we always prefer using mkstemp()...
+    #if wxUSE_FILE
+        if ( fileTemp && !fileTemp->IsOpened() )
+        {
+            *deleteOnClose = wantDeleteOnClose;
+            int fd = wxTempOpen(path, deleteOnClose);
+            if (fd != -1)
+                fileTemp->Attach(fd);
+            else
+                ok = false;
+        }
+    #endif
+
+    #if wxUSE_FFILE
+        if ( ffileTemp && !ffileTemp->IsOpened() )
+        {
+            *deleteOnClose = wantDeleteOnClose;
+            ok = wxTempOpen(ffileTemp, path, deleteOnClose);
+        }
+    #endif
+
+        if ( !ok )
+        {
+            // FIXME: If !ok here should we loop and try again with another
+            //        file name?  That is the standard recourse if open(O_EXCL)
+            //        fails, though of course it should be protected against
+            //        possible infinite looping too.
+
+            wxLogError(_("Failed to open temporary file."));
+
+            path.clear();
+        }
+    }
+
+    return path;
+}
+
+
+static bool wxCreateTempImpl(
+        const wxString& prefix,
+        WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp),
+        wxString *name)
+{
+    bool deleteOnClose = true;
+
+    *name = wxCreateTempImpl(prefix,
+                             WXFILEARGS(fileTemp, ffileTemp),
+                             &deleteOnClose);
+
+    bool ok = !name->empty();
+
+    if (deleteOnClose)
+        name->clear();
+#ifdef __UNIX__
+    else if (ok && wxRemoveFile(*name))
+        name->clear();
+#endif
+
+    return ok;
+}
+
+
+static void wxAssignTempImpl(
+        wxFileName *fn,
+        const wxString& prefix,
+        WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp))
+{
+    wxString tempname;
+    tempname = wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, ffileTemp));
+
+    if ( tempname.empty() )
+    {
+        // error, failed to get temp file name
+        fn->Clear();
+    }
+    else // ok
+    {
+        fn->Assign(tempname);
+    }
+}
+
+
+void wxFileName::AssignTempFileName(const wxString& prefix)
+{
+    wxAssignTempImpl(this, prefix, WXFILEARGS(NULL, NULL));
+}
+
+/* static */
+wxString wxFileName::CreateTempFileName(const wxString& prefix)
+{
+    return wxCreateTempImpl(prefix, WXFILEARGS(NULL, NULL));
+}
+
+#endif // wxUSE_FILE || wxUSE_FFILE
+
+
+#if wxUSE_FILE
+
+wxString wxCreateTempFileName(const wxString& prefix,
+                              wxFile *fileTemp,
+                              bool *deleteOnClose)
+{
+    return wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, NULL), deleteOnClose);
+}
+
+bool wxCreateTempFile(const wxString& prefix,
+                      wxFile *fileTemp,
+                      wxString *name)
+{
+    return wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, NULL), name);
+}
+
+void wxFileName::AssignTempFileName(const wxString& prefix, wxFile *fileTemp)
+{
+    wxAssignTempImpl(this, prefix, WXFILEARGS(fileTemp, NULL));
+}
+
+/* static */
+wxString
+wxFileName::CreateTempFileName(const wxString& prefix, wxFile *fileTemp)
+{
+    return wxCreateTempFileName(prefix, fileTemp);
+}
+
+#endif // wxUSE_FILE
+
+
+#if wxUSE_FFILE
+
+wxString wxCreateTempFileName(const wxString& prefix,
+                              wxFFile *fileTemp,
+                              bool *deleteOnClose)
+{
+    return wxCreateTempImpl(prefix, WXFILEARGS(NULL, fileTemp), deleteOnClose);
+}
+
+bool wxCreateTempFile(const wxString& prefix,
+                      wxFFile *fileTemp,
+                      wxString *name)
+{
+    return wxCreateTempImpl(prefix, WXFILEARGS(NULL, fileTemp), name);
+
+}
+
+void wxFileName::AssignTempFileName(const wxString& prefix, wxFFile *fileTemp)
+{
+    wxAssignTempImpl(this, prefix, WXFILEARGS(NULL, fileTemp));
+}
+
+/* static */
+wxString
+wxFileName::CreateTempFileName(const wxString& prefix, wxFFile *fileTemp)
+{
+    return wxCreateTempFileName(prefix, fileTemp);
+}
+
+#endif // wxUSE_FFILE
 
 
-    return path;
-}
 
 // ----------------------------------------------------------------------------
 // directory operations
 // ----------------------------------------------------------------------------
 
 
 // ----------------------------------------------------------------------------
 // directory operations
 // ----------------------------------------------------------------------------
 
-bool wxFileName::Mkdir( int perm, bool full )
+// helper of GetTempDir(): check if the given directory exists and return it if
+// it does or an empty string otherwise
+namespace
 {
 {
-    return wxFileName::Mkdir( GetFullPath(), perm, full );
+
+wxString CheckIfDirExists(const wxString& dir)
+{
+    return wxFileName::DirExists(dir) ? dir : wxString();
 }
 
 }
 
-bool wxFileName::Mkdir( const wxString &dir, int perm, bool full )
+} // anonymous namespace
+
+wxString wxFileName::GetTempDir()
 {
 {
-    if (full)
+    // first try getting it from environment: this allows overriding the values
+    // used by default if the user wants to create temporary files in another
+    // directory
+    wxString dir = CheckIfDirExists(wxGetenv("TMPDIR"));
+    if ( dir.empty() )
     {
     {
-        wxFileName filename(dir);
-        wxArrayString dirs = filename.GetDirs();
-        dirs.Add(filename.GetName());
+        dir = CheckIfDirExists(wxGetenv("TMP"));
+        if ( dir.empty() )
+            dir = CheckIfDirExists(wxGetenv("TEMP"));
+    }
+
+    // if no environment variables are set, use the system default
+    if ( dir.empty() )
+    {
+#if defined(__WXWINCE__)
+        dir = CheckIfDirExists(wxT("\\temp"));
+#elif defined(__WINDOWS__) && !defined(__WXMICROWIN__)
+        if ( !::GetTempPath(MAX_PATH, wxStringBuffer(dir, MAX_PATH + 1)) )
+        {
+            wxLogLastError(wxT("GetTempPath"));
+        }
+#elif defined(__WXMAC__) && wxOSX_USE_CARBON
+        dir = wxMacFindFolderNoSeparator(short(kOnSystemDisk), kTemporaryFolderType, kCreateFolder);
+#endif // systems with native way
+    }
+    else // we got directory from an environment variable
+    {
+        // remove any trailing path separators, we don't want to ever return
+        // them from this function for consistency
+        const size_t lastNonSep = dir.find_last_not_of(GetPathSeparators());
+        if ( lastNonSep == wxString::npos )
+        {
+            // the string consists entirely of separators, leave only one
+            dir = GetPathSeparator();
+        }
+        else
+        {
+            dir.erase(lastNonSep + 1);
+        }
+    }
+
+    // fall back to hard coded value
+    if ( dir.empty() )
+    {
+#ifdef __UNIX_LIKE__
+        dir = CheckIfDirExists("/tmp");
+        if ( dir.empty() )
+#endif // __UNIX_LIKE__
+            dir = ".";
+    }
+
+    return dir;
+}
+
+bool wxFileName::Mkdir( int perm, int flags ) const
+{
+    return wxFileName::Mkdir(GetPath(), perm, flags);
+}
+
+bool wxFileName::Mkdir( const wxString& dir, int perm, int flags )
+{
+    if ( flags & wxPATH_MKDIR_FULL )
+    {
+        // split the path in components
+        wxFileName filename;
+        filename.AssignDir(dir);
 
 
-        size_t count = dirs.GetCount();
-        size_t i;
         wxString currPath;
         wxString currPath;
-        int noErrors = 0;
-        for ( i = 0; i < count; i++ )
+        if ( filename.HasVolume())
         {
         {
-            currPath += dirs[i];
+            currPath << wxGetVolumeString(filename.GetVolume(), wxPATH_NATIVE);
+        }
 
 
-            if (currPath.Last() == wxT(':'))
-            {
-                // Can't create a root directory so continue to next dir
+        wxArrayString dirs = filename.GetDirs();
+        size_t count = dirs.GetCount();
+        for ( size_t i = 0; i < count; i++ )
+        {
+            if ( i > 0 || filename.IsAbsolute() )
                 currPath += wxFILE_SEP_PATH;
                 currPath += wxFILE_SEP_PATH;
-                continue;
-            }
+            currPath += dirs[i];
 
             if (!DirExists(currPath))
 
             if (!DirExists(currPath))
+            {
                 if (!wxMkdir(currPath, perm))
                 if (!wxMkdir(currPath, perm))
-                    noErrors ++;
-
-            if ( (i < (count-1)) )
-                currPath += wxFILE_SEP_PATH;
+                {
+                    // no need to try creating further directories
+                    return false;
+                }
+            }
         }
 
         }
 
-        return (noErrors == 0);
+        return true;
 
     }
 
     }
-    else
-        return ::wxMkdir( dir, perm );
+
+    return ::wxMkdir( dir, perm );
 }
 
 }
 
-bool wxFileName::Rmdir()
+bool wxFileName::Rmdir(int flags) const
 {
 {
-    return wxFileName::Rmdir( GetFullPath() );
+    return wxFileName::Rmdir( GetPath(), flags );
 }
 
 }
 
-bool wxFileName::Rmdir( const wxString &dir )
+bool wxFileName::Rmdir(const wxString& dir, int flags)
 {
 {
-    return ::wxRmdir( dir );
+#ifdef __WXMSW__
+    if ( flags & wxPATH_RMDIR_RECURSIVE )
+    {
+        // SHFileOperation needs double null termination string
+        // but without separator at the end of the path
+        wxString path(dir);
+        if ( path.Last() == wxFILE_SEP_PATH )
+            path.RemoveLast();
+        path += wxT('\0');
+
+        SHFILEOPSTRUCT fileop;
+        wxZeroMemory(fileop);
+        fileop.wFunc = FO_DELETE;
+    #if defined(__CYGWIN__) && defined(wxUSE_UNICODE)
+        fileop.pFrom = path.wc_str();
+    #else
+        fileop.pFrom = path.fn_str();
+    #endif
+        fileop.fFlags = FOF_SILENT | FOF_NOCONFIRMATION;
+    #ifndef __WXWINCE__
+        // FOF_NOERRORUI is not defined in WinCE
+        fileop.fFlags |= FOF_NOERRORUI;
+    #endif
+
+        int ret = SHFileOperation(&fileop);
+        if ( ret != 0 )
+        {
+            // SHFileOperation may return non-Win32 error codes, so the error
+            // message can be incorrect
+            wxLogApiError(wxT("SHFileOperation"), ret);
+            return false;
+        }
+
+        return true;
+    }
+    else if ( flags & wxPATH_RMDIR_FULL )
+#else // !__WXMSW__
+    if ( flags != 0 )   // wxPATH_RMDIR_FULL or wxPATH_RMDIR_RECURSIVE
+#endif // !__WXMSW__
+    {
+        wxString path(dir);
+        if ( path.Last() != wxFILE_SEP_PATH )
+            path += wxFILE_SEP_PATH;
+
+        wxDir d(path);
+
+        if ( !d.IsOpened() )
+            return false;
+
+        wxString filename;
+
+        // first delete all subdirectories
+        bool cont = d.GetFirst(&filename, "", wxDIR_DIRS | wxDIR_HIDDEN);
+        while ( cont )
+        {
+            wxFileName::Rmdir(path + filename, flags);
+            cont = d.GetNext(&filename);
+        }
+
+#ifndef __WXMSW__
+        if ( flags & wxPATH_RMDIR_RECURSIVE )
+        {
+            // delete all files too
+            cont = d.GetFirst(&filename, "", wxDIR_FILES | wxDIR_HIDDEN);
+            while ( cont )
+            {
+                ::wxRemoveFile(path + filename);
+                cont = d.GetNext(&filename);
+            }
+        }
+#endif // !__WXMSW__
+    }
+
+    return ::wxRmdir(dir);
 }
 
 // ----------------------------------------------------------------------------
 // path normalization
 // ----------------------------------------------------------------------------
 
 }
 
 // ----------------------------------------------------------------------------
 // path normalization
 // ----------------------------------------------------------------------------
 
-bool wxFileName::Normalize(wxPathNormalize flags,
+bool wxFileName::Normalize(int flags,
                            const wxString& cwd,
                            wxPathFormat format)
 {
                            const wxString& cwd,
                            wxPathFormat format)
 {
+    // deal with env vars renaming first as this may seriously change the path
+    if ( flags & wxPATH_NORM_ENV_VARS )
+    {
+        wxString pathOrig = GetFullPath(format);
+        wxString path = wxExpandEnvVars(pathOrig);
+        if ( path != pathOrig )
+        {
+            Assign(path);
+        }
+    }
+
     // the existing path components
     wxArrayString dirs = GetDirs();
 
     // the existing path components
     wxArrayString dirs = GetDirs();
 
@@ -648,8 +1366,8 @@ bool wxFileName::Normalize(wxPathNormalize flags,
 
     format = GetFormat(format);
 
 
     format = GetFormat(format);
 
-    // make the path absolute
-    if ( (flags & wxPATH_NORM_ABSOLUTE) && !IsAbsolute() )
+    // set up the directory to use for making the path absolute later
+    if ( (flags & wxPATH_NORM_ABSOLUTE) && !IsAbsolute(format) )
     {
         if ( cwd.empty() )
         {
     {
         if ( cwd.empty() )
         {
@@ -659,32 +1377,18 @@ bool wxFileName::Normalize(wxPathNormalize flags,
         {
             curDir.AssignDir(cwd);
         }
         {
             curDir.AssignDir(cwd);
         }
-
-        // the path may be not absolute because it doesn't have the volume name
-        // but in this case we shouldn't modify the directory components of it
-        // but just set the current volume
-        if ( !HasVolume() && curDir.HasVolume() )
-        {
-            SetVolume(curDir.GetVolume());
-
-            if ( IsAbsolute() )
-            {
-                // yes, it was the case - we don't need curDir then
-                curDir.Clear();
-            }
-        }
     }
 
     // handle ~ stuff under Unix only
     }
 
     // handle ~ stuff under Unix only
-    if ( (format == wxPATH_UNIX) && (flags & wxPATH_NORM_TILDE) )
+    if ( (format == wxPATH_UNIX) && (flags & wxPATH_NORM_TILDE) && m_relative )
     {
         if ( !dirs.IsEmpty() )
         {
             wxString dir = dirs[0u];
     {
         if ( !dirs.IsEmpty() )
         {
             wxString dir = dirs[0u];
-            if ( !dir.empty() && dir[0u] == _T('~') )
+            if ( !dir.empty() && dir[0u] == wxT('~') )
             {
             {
+                // to make the path absolute use the home directory
                 curDir.AssignDir(wxGetUserHome(dir.c_str() + 1));
                 curDir.AssignDir(wxGetUserHome(dir.c_str() + 1));
-
                 dirs.RemoveAt(0u);
             }
         }
                 dirs.RemoveAt(0u);
             }
         }
@@ -693,14 +1397,34 @@ bool wxFileName::Normalize(wxPathNormalize flags,
     // transform relative path into abs one
     if ( curDir.IsOk() )
     {
     // transform relative path into abs one
     if ( curDir.IsOk() )
     {
-        wxArrayString dirsNew = curDir.GetDirs();
-        size_t count = dirs.GetCount();
-        for ( size_t n = 0; n < count; n++ )
+        // this path may be relative because it doesn't have the volume name
+        // and still have m_relative=true; in this case we shouldn't modify
+        // our directory components but just set the current volume
+        if ( !HasVolume() && curDir.HasVolume() )
+        {
+            SetVolume(curDir.GetVolume());
+
+            if ( !m_relative )
         {
         {
-            dirsNew.Add(dirs[n]);
+                // yes, it was the case - we don't need curDir then
+                curDir.Clear();
+            }
         }
 
         }
 
-        dirs = dirsNew;
+        // finally, prepend curDir to the dirs array
+        wxArrayString dirsNew = curDir.GetDirs();
+        WX_PREPEND_ARRAY(dirs, dirsNew);
+
+        // if we used e.g. tilde expansion previously and wxGetUserHome didn't
+        // return for some reason an absolute path, then curDir maybe not be absolute!
+        if ( !curDir.m_relative )
+        {
+            // we have prepended an absolute path and thus we are now an absolute
+            // file name too
+            m_relative = false;
+        }
+        // else if (flags & wxPATH_NORM_ABSOLUTE):
+        //   should we warn the user that we didn't manage to make the path absolute?
     }
 
     // now deal with ".", ".." and the rest
     }
 
     // now deal with ".", ".." and the rest
@@ -720,57 +1444,239 @@ bool wxFileName::Normalize(wxPathNormalize flags,
 
             if ( dir == wxT("..") )
             {
 
             if ( dir == wxT("..") )
             {
-                if ( m_dirs.IsEmpty() )
+                if ( m_dirs.empty() )
                 {
                 {
-                    wxLogError(_("The path '%s' contains too many \"..\"!"),
-                               GetFullPath().c_str());
-                    return FALSE;
-                }
+                    // We have more ".." than directory components so far.
+                    // Don't treat this as an error as the path could have been
+                    // entered by user so try to handle it reasonably: if the
+                    // path is absolute, just ignore the extra ".." because
+                    // "/.." is the same as "/". Otherwise, i.e. for relative
+                    // paths, keep ".." unchanged because removing it would
+                    // modify the file a relative path refers to.
+                    if ( !m_relative )
+                        continue;
 
 
-                m_dirs.RemoveAt(m_dirs.GetCount() - 1);
-                continue;
+                }
+                else // Normal case, go one step up.
+                {
+                    m_dirs.pop_back();
+                    continue;
+                }
             }
         }
 
             }
         }
 
-        if ( flags & wxPATH_NORM_ENV_VARS )
-        {
-            dir = wxExpandEnvVars(dir);
-        }
+        m_dirs.Add(dir);
+    }
 
 
-        if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) )
+#if defined(__WIN32__) && !defined(__WXWINCE__) && wxUSE_OLE
+    if ( (flags & wxPATH_NORM_SHORTCUT) )
+    {
+        wxString filename;
+        if (GetShortcutTarget(GetFullPath(format), filename))
         {
         {
-            dir.MakeLower();
+            m_relative = false;
+            Assign(filename);
         }
         }
+    }
+#endif
 
 
-        m_dirs.Add(dir);
+#if defined(__WIN32__)
+    if ( (flags & wxPATH_NORM_LONG) && (format == wxPATH_DOS) )
+    {
+        Assign(GetLongPath());
     }
     }
+#endif // Win32
 
 
+    // Change case  (this should be kept at the end of the function, to ensure
+    // that the path doesn't change any more after we normalize its case)
     if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) )
     {
     if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) )
     {
-        // VZ: expand env vars here too?
-
+        m_volume.MakeLower();
         m_name.MakeLower();
         m_ext.MakeLower();
         m_name.MakeLower();
         m_ext.MakeLower();
+
+        // directory entries must be made lower case as well
+        count = m_dirs.GetCount();
+        for ( size_t i = 0; i < count; i++ )
+        {
+            m_dirs[i].MakeLower();
+        }
     }
 
     }
 
-#if defined(__WIN32__)
-    if ( (flags & wxPATH_NORM_LONG) && (format == wxPATH_DOS) )
+    return true;
+}
+
+#ifndef __WXWINCE__
+bool wxFileName::ReplaceEnvVariable(const wxString& envname,
+                                    const wxString& replacementFmtString,
+                                    wxPathFormat format)
+{
+    // look into stringForm for the contents of the given environment variable
+    wxString val;
+    if (envname.empty() ||
+        !wxGetEnv(envname, &val))
+        return false;
+    if (val.empty())
+        return false;
+
+    wxString stringForm = GetPath(wxPATH_GET_VOLUME, format);
+        // do not touch the file name and the extension
+
+    wxString replacement = wxString::Format(replacementFmtString, envname);
+    stringForm.Replace(val, replacement);
+
+    // Now assign ourselves the modified path:
+    Assign(stringForm, GetFullName(), format);
+
+    return true;
+}
+#endif
+
+bool wxFileName::ReplaceHomeDir(wxPathFormat format)
+{
+    wxString homedir = wxGetHomeDir();
+    if (homedir.empty())
+        return false;
+
+    wxString stringForm = GetPath(wxPATH_GET_VOLUME, format);
+        // do not touch the file name and the extension
+
+    stringForm.Replace(homedir, "~");
+
+    // Now assign ourselves the modified path:
+    Assign(stringForm, GetFullName(), format);
+
+    return true;
+}
+
+// ----------------------------------------------------------------------------
+// get the shortcut target
+// ----------------------------------------------------------------------------
+
+// WinCE (3) doesn't have CLSID_ShellLink, IID_IShellLink definitions.
+// The .lnk file is a plain text file so it should be easy to
+// make it work. Hint from Google Groups:
+// "If you open up a lnk file, you'll see a
+// number, followed by a pound sign (#), followed by more text. The
+// number is the number of characters that follows the pound sign. The
+// characters after the pound sign are the command line (which _can_
+// include arguments) to be executed. Any path (e.g. \windows\program
+// files\myapp.exe) that includes spaces needs to be enclosed in
+// quotation marks."
+
+#if defined(__WIN32__) && !defined(__WXWINCE__) && wxUSE_OLE
+// The following lines are necessary under WinCE
+// #include "wx/msw/private.h"
+// #include <ole2.h>
+#include <shlobj.h>
+#if defined(__WXWINCE__)
+#include <shlguid.h>
+#endif
+
+bool wxFileName::GetShortcutTarget(const wxString& shortcutPath,
+                                   wxString& targetFilename,
+                                   wxString* arguments) const
+{
+    wxString path, file, ext;
+    wxFileName::SplitPath(shortcutPath, & path, & file, & ext);
+
+    HRESULT hres;
+    IShellLink* psl;
+    bool success = false;
+
+    // Assume it's not a shortcut if it doesn't end with lnk
+    if (ext.CmpNoCase(wxT("lnk"))!=0)
+        return false;
+
+    // create a ShellLink object
+    hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+                            IID_IShellLink, (LPVOID*) &psl);
+
+    if (SUCCEEDED(hres))
     {
     {
-        Assign(GetLongPath());
+        IPersistFile* ppf;
+        hres = psl->QueryInterface( IID_IPersistFile, (LPVOID *) &ppf);
+        if (SUCCEEDED(hres))
+        {
+            WCHAR wsz[MAX_PATH];
+
+            MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, shortcutPath.mb_str(), -1, wsz,
+                                MAX_PATH);
+
+            hres = ppf->Load(wsz, 0);
+            ppf->Release();
+
+            if (SUCCEEDED(hres))
+            {
+                wxChar buf[2048];
+                // Wrong prototype in early versions
+#if defined(__MINGW32__) && !wxCHECK_W32API_VERSION(2, 2)
+                psl->GetPath((CHAR*) buf, 2048, NULL, SLGP_UNCPRIORITY);
+#else
+                psl->GetPath(buf, 2048, NULL, SLGP_UNCPRIORITY);
+#endif
+                targetFilename = wxString(buf);
+                success = (shortcutPath != targetFilename);
+
+                psl->GetArguments(buf, 2048);
+                wxString args(buf);
+                if (!args.empty() && arguments)
+                {
+                    *arguments = args;
+                }
+            }
+        }
+
+        psl->Release();
+    }
+    return success;
+}
+
+#endif // __WIN32__ && !__WXWINCE__
+
+
+// ----------------------------------------------------------------------------
+// absolute/relative paths
+// ----------------------------------------------------------------------------
+
+bool wxFileName::IsAbsolute(wxPathFormat format) const
+{
+    // unix paths beginning with ~ are reported as being absolute
+    if ( format == wxPATH_UNIX )
+    {
+        if ( !m_dirs.IsEmpty() )
+        {
+            wxString dir = m_dirs[0u];
+
+            if (!dir.empty() && dir[0u] == wxT('~'))
+                return true;
+        }
+    }
+
+    // if our path doesn't start with a path separator, it's not an absolute
+    // path
+    if ( m_relative )
+        return false;
+
+    if ( !GetVolumeSeparator(format).empty() )
+    {
+        // this format has volumes and an absolute path must have one, it's not
+        // enough to have the full path to be an absolute file under Windows
+        if ( GetVolume().empty() )
+            return false;
     }
     }
-#endif // Win32
 
 
-    return TRUE;
+    return true;
 }
 
 bool wxFileName::MakeRelativeTo(const wxString& pathBase, wxPathFormat format)
 {
 }
 
 bool wxFileName::MakeRelativeTo(const wxString& pathBase, wxPathFormat format)
 {
-    wxFileName fnBase(pathBase, format);
+    wxFileName fnBase = wxFileName::DirName(pathBase, format);
 
     // get cwd only once - small time saving
     wxString cwd = wxGetCwd();
 
     // get cwd only once - small time saving
     wxString cwd = wxGetCwd();
-    Normalize(wxPATH_NORM_ALL, cwd, format);
-    fnBase.Normalize(wxPATH_NORM_ALL, cwd, format);
+    Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, cwd, format);
+    fnBase.Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, cwd, format);
 
     bool withCase = IsCaseSensitive(format);
 
 
     bool withCase = IsCaseSensitive(format);
 
@@ -778,7 +1684,7 @@ bool wxFileName::MakeRelativeTo(const wxString& pathBase, wxPathFormat format)
     if ( !GetVolume().IsSameAs(fnBase.GetVolume(), withCase) )
     {
         // nothing done
     if ( !GetVolume().IsSameAs(fnBase.GetVolume(), withCase) )
     {
         // nothing done
-        return FALSE;
+        return false;
     }
 
     // same drive, so we don't need our volume
     }
 
     // same drive, so we don't need our volume
@@ -799,31 +1705,44 @@ bool wxFileName::MakeRelativeTo(const wxString& pathBase, wxPathFormat format)
         m_dirs.Insert(wxT(".."), 0u);
     }
 
         m_dirs.Insert(wxT(".."), 0u);
     }
 
+    if ( format == wxPATH_UNIX || format == wxPATH_DOS )
+    {
+        // a directory made relative with respect to itself is '.' under Unix
+        // and DOS, by definition (but we don't have to insert "./" for the
+        // files)
+        if ( m_dirs.IsEmpty() && IsDir() )
+        {
+            m_dirs.Add(wxT('.'));
+        }
+    }
+
+    m_relative = true;
+
     // we were modified
     // we were modified
-    return TRUE;
+    return true;
 }
 
 // ----------------------------------------------------------------------------
 // filename kind tests
 // ----------------------------------------------------------------------------
 
 }
 
 // ----------------------------------------------------------------------------
 // filename kind tests
 // ----------------------------------------------------------------------------
 
-bool wxFileName::SameAs(const wxFileName &filepath, wxPathFormat format)
+bool wxFileName::SameAs(const wxFileName& filepath, wxPathFormat format) const
 {
     wxFileName fn1 = *this,
                fn2 = filepath;
 
     // get cwd only once - small time saving
     wxString cwd = wxGetCwd();
 {
     wxFileName fn1 = *this,
                fn2 = filepath;
 
     // get cwd only once - small time saving
     wxString cwd = wxGetCwd();
-    fn1.Normalize(wxPATH_NORM_ALL, cwd, format);
-    fn2.Normalize(wxPATH_NORM_ALL, cwd, format);
+    fn1.Normalize(wxPATH_NORM_ALL | wxPATH_NORM_CASE, cwd, format);
+    fn2.Normalize(wxPATH_NORM_ALL | wxPATH_NORM_CASE, cwd, format);
 
     if ( fn1.GetFullPath() == fn2.GetFullPath() )
 
     if ( fn1.GetFullPath() == fn2.GetFullPath() )
-        return TRUE;
+        return true;
 
     // TODO: compare inodes for Unix, this works even when filenames are
     //       different but files are the same (symlinks) (VZ)
 
 
     // TODO: compare inodes for Unix, this works even when filenames are
     //       different but files are the same (symlinks) (VZ)
 
-    return FALSE;
+    return false;
 }
 
 /* static */
 }
 
 /* static */
@@ -833,59 +1752,47 @@ bool wxFileName::IsCaseSensitive( wxPathFormat format )
     return GetFormat(format) == wxPATH_UNIX;
 }
 
     return GetFormat(format) == wxPATH_UNIX;
 }
 
-bool wxFileName::IsAbsolute( wxPathFormat format )
+/* static */
+wxString wxFileName::GetForbiddenChars(wxPathFormat format)
 {
 {
-    // if we have no path, we can't be an abs filename
-    if ( m_dirs.IsEmpty() )
-    {
-        return FALSE;
-    }
-
-    format = GetFormat(format);
-
-    if ( format == wxPATH_UNIX )
-    {
-        const wxString& str = m_dirs[0u];
-        if ( str.empty() )
-        {
-            // the path started with '/', it's an absolute one
-            return TRUE;
-        }
-
-        // the path is absolute if it starts with a path separator or
-        // with "~" or "~user"
-        wxChar ch = str[0u];
+    // Inits to forbidden characters that are common to (almost) all platforms.
+    wxString strForbiddenChars = wxT("*?");
 
 
-        return IsPathSeparator(ch, format) || ch == _T('~');
-    }
-    else // !Unix
+    // If asserts, wxPathFormat has been changed. In case of a new path format
+    // addition, the following code might have to be updated.
+    wxCOMPILE_TIME_ASSERT(wxPATH_MAX == 5, wxPathFormatChanged);
+    switch ( GetFormat(format) )
     {
     {
-        // must have the drive
-        if ( m_volume.empty() )
-            return FALSE;
+        default :
+            wxFAIL_MSG( wxT("Unknown path format") );
+            // !! Fall through !!
 
 
-        switch ( format )
-        {
-            default:
-                wxFAIL_MSG( _T("unknown wxPATH_XXX style") );
-                // fall through
+        case wxPATH_UNIX:
+            break;
 
 
-            case wxPATH_DOS:
-                return m_dirs[0u].empty();
+        case wxPATH_MAC:
+            // On a Mac even names with * and ? are allowed (Tested with OS
+            // 9.2.1 and OS X 10.2.5)
+            strForbiddenChars = wxEmptyString;
+            break;
 
 
-            case wxPATH_VMS:
-                // TODO: what is the relative path format here?
-                return TRUE;
+        case wxPATH_DOS:
+            strForbiddenChars += wxT("\\/:\"<>|");
+            break;
 
 
-            case wxPATH_MAC:
-                return !m_dirs[0u].empty();
-        }
+        case wxPATH_VMS:
+            break;
     }
     }
+
+    return strForbiddenChars;
 }
 
 /* static */
 }
 
 /* static */
-wxString wxFileName::GetVolumeSeparator(wxPathFormat format)
+wxString wxFileName::GetVolumeSeparator(wxPathFormat WXUNUSED_IN_WINCE(format))
 {
 {
+#ifdef __WXWINCE__
+    return wxEmptyString;
+#else
     wxString sepVol;
 
     if ( (GetFormat(format) == wxPATH_DOS) ||
     wxString sepVol;
 
     if ( (GetFormat(format) == wxPATH_DOS) ||
@@ -896,6 +1803,7 @@ wxString wxFileName::GetVolumeSeparator(wxPathFormat format)
     //else: leave empty
 
     return sepVol;
     //else: leave empty
 
     return sepVol;
+#endif
 }
 
 /* static */
 }
 
 /* static */
@@ -911,7 +1819,7 @@ wxString wxFileName::GetPathSeparators(wxPathFormat format)
             break;
 
         default:
             break;
 
         default:
-            wxFAIL_MSG( _T("unknown wxPATH_XXX style") );
+            wxFAIL_MSG( wxT("Unknown wxPATH_XXX style") );
             // fall through
 
         case wxPATH_UNIX:
             // fall through
 
         case wxPATH_UNIX:
@@ -930,46 +1838,83 @@ wxString wxFileName::GetPathSeparators(wxPathFormat format)
     return seps;
 }
 
     return seps;
 }
 
+/* static */
+wxString wxFileName::GetPathTerminators(wxPathFormat format)
+{
+    format = GetFormat(format);
+
+    // under VMS the end of the path is ']', not the path separator used to
+    // separate the components
+    return format == wxPATH_VMS ? wxString(wxT(']')) : GetPathSeparators(format);
+}
+
 /* static */
 bool wxFileName::IsPathSeparator(wxChar ch, wxPathFormat format)
 {
     // wxString::Find() doesn't work as expected with NUL - it will always find
 /* static */
 bool wxFileName::IsPathSeparator(wxChar ch, wxPathFormat format)
 {
     // wxString::Find() doesn't work as expected with NUL - it will always find
-    // it, so it is almost surely a bug if this function is called with NUL arg
-    wxASSERT_MSG( ch != _T('\0'), _T("shouldn't be called with NUL") );
-
-    return GetPathSeparators(format).Find(ch) != wxNOT_FOUND;
+    // it, so test for it separately
+    return ch != wxT('\0') && GetPathSeparators(format).Find(ch) != wxNOT_FOUND;
 }
 
 }
 
-bool wxFileName::IsWild( wxPathFormat format )
+/* static */
+bool
+wxFileName::IsMSWUniqueVolumeNamePath(const wxString& path, wxPathFormat format)
 {
 {
-    // FIXME: this is probably false for Mac and this is surely wrong for most
-    //        of Unix shells (think about "[...]")
-    (void)format;
-    return m_name.find_first_of(_T("*?")) != wxString::npos;
+    // return true if the format used is the DOS/Windows one and the string begins
+    // with a Windows unique volume name ("\\?\Volume{guid}\")
+    return format == wxPATH_DOS &&
+            path.length() >= wxMSWUniqueVolumePrefixLength &&
+             path.StartsWith(wxS("\\\\?\\Volume{")) &&
+              path[wxMSWUniqueVolumePrefixLength - 1] == wxFILE_SEP_PATH_DOS;
 }
 
 // ----------------------------------------------------------------------------
 // path components manipulation
 // ----------------------------------------------------------------------------
 
 }
 
 // ----------------------------------------------------------------------------
 // path components manipulation
 // ----------------------------------------------------------------------------
 
-void wxFileName::AppendDir( const wxString &dir )
+/* static */ bool wxFileName::IsValidDirComponent(const wxString& dir)
+{
+    if ( dir.empty() )
+    {
+        wxFAIL_MSG( wxT("empty directory passed to wxFileName::InsertDir()") );
+
+        return false;
+    }
+
+    const size_t len = dir.length();
+    for ( size_t n = 0; n < len; n++ )
+    {
+        if ( dir[n] == GetVolumeSeparator() || IsPathSeparator(dir[n]) )
+        {
+            wxFAIL_MSG( wxT("invalid directory component in wxFileName") );
+
+            return false;
+        }
+    }
+
+    return true;
+}
+
+void wxFileName::AppendDir( const wxString& dir )
 {
 {
-    m_dirs.Add( dir );
+    if ( IsValidDirComponent(dir) )
+        m_dirs.Add( dir );
 }
 
 }
 
-void wxFileName::PrependDir( const wxString &dir )
+void wxFileName::PrependDir( const wxStringdir )
 {
 {
-    m_dirs.Insert( dir, 0 );
+    InsertDir(0, dir);
 }
 
 }
 
-void wxFileName::InsertDir( int before, const wxString &dir )
+void wxFileName::InsertDir(size_t before, const wxString& dir)
 {
 {
-    m_dirs.Insert( dir, before );
+    if ( IsValidDirComponent(dir) )
+        m_dirs.Insert(dir, before);
 }
 
 }
 
-void wxFileName::RemoveDir( int pos )
+void wxFileName::RemoveDir(size_t pos)
 {
 {
-    m_dirs.Remove( (size_t)pos );
+    m_dirs.RemoveAt(pos);
 }
 
 // ----------------------------------------------------------------------------
 }
 
 // ----------------------------------------------------------------------------
@@ -978,13 +1923,14 @@ void wxFileName::RemoveDir( int pos )
 
 void wxFileName::SetFullName(const wxString& fullname)
 {
 
 void wxFileName::SetFullName(const wxString& fullname)
 {
-    SplitPath(fullname, NULL /* no path */, &m_name, &m_ext);
+    SplitPath(fullname, NULL /* no volume */, NULL /* no path */,
+                        &m_name, &m_ext, &m_hasExt);
 }
 
 wxString wxFileName::GetFullName() const
 {
     wxString fullname = m_name;
 }
 
 wxString wxFileName::GetFullName() const
 {
     wxString fullname = m_name;
-    if ( !m_ext.empty() )
+    if ( m_hasExt )
     {
         fullname << wxFILE_SEP_EXT << m_ext;
     }
     {
         fullname << wxFILE_SEP_EXT << m_ext;
     }
@@ -992,88 +1938,117 @@ wxString wxFileName::GetFullName() const
     return fullname;
 }
 
     return fullname;
 }
 
-wxString wxFileName::GetPath( bool add_separator, wxPathFormat format ) const
+wxString wxFileName::GetPath( int flags, wxPathFormat format ) const
 {
     format = GetFormat( format );
 
 {
     format = GetFormat( format );
 
-    wxString ret;
-    size_t count = m_dirs.GetCount();
-    for ( size_t i = 0; i < count; i++ )
+    wxString fullpath;
+
+    // return the volume with the path as well if requested
+    if ( flags & wxPATH_GET_VOLUME )
     {
     {
-        ret += m_dirs[i];
-        if ( add_separator || (i < count) )
-            ret += wxFILE_SEP_PATH;
+        fullpath += wxGetVolumeString(GetVolume(), format);
     }
 
     }
 
-    return ret;
-}
+    // the leading character
+    switch ( format )
+    {
+        case wxPATH_MAC:
+            if ( m_relative )
+                fullpath += wxFILE_SEP_PATH_MAC;
+            break;
 
 
-wxString wxFileName::GetFullPath( wxPathFormat format ) const
-{
-    format = GetFormat(format);
+        case wxPATH_DOS:
+            if ( !m_relative )
+                fullpath += wxFILE_SEP_PATH_DOS;
+            break;
 
 
-    wxString fullpath;
+        default:
+            wxFAIL_MSG( wxT("Unknown path format") );
+            // fall through
 
 
-    // first put the volume
-    if ( !m_volume.empty() )
-    {
-       {
-               // Special Windows UNC paths hack, part 2: undo what we did in
-               // SplitPath() and make an UNC path if we have a drive which is not a
-               // single letter (hopefully the network shares can't be one letter only
-               // although I didn't find any authoritative docs on this)
-               if ( format == wxPATH_DOS && m_volume.length() > 1 )
-               {
-               fullpath << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_DOS << m_volume;
-               }
-               else // !UNC
-               {
-               fullpath << m_volume << GetVolumeSeparator(format);
+        case wxPATH_UNIX:
+            if ( !m_relative )
+            {
+                fullpath += wxFILE_SEP_PATH_UNIX;
             }
             }
-        }
+            break;
+
+        case wxPATH_VMS:
+            // no leading character here but use this place to unset
+            // wxPATH_GET_SEPARATOR flag: under VMS it doesn't make sense
+            // as, if I understand correctly, there should never be a dot
+            // before the closing bracket
+            flags &= ~wxPATH_GET_SEPARATOR;
+    }
+
+    if ( m_dirs.empty() )
+    {
+        // there is nothing more
+        return fullpath;
     }
 
     // then concatenate all the path components using the path separator
     }
 
     // then concatenate all the path components using the path separator
-    size_t dirCount = m_dirs.GetCount();
-    if ( dirCount )
+    if ( format == wxPATH_VMS )
     {
     {
-        // under Mac, we must have a path separator in the beginning of the
-        // relative path - otherwise it would be parsed as an absolute one
-        if ( format == wxPATH_MAC && m_dirs[0].empty() )
-        {
-            fullpath += wxFILE_SEP_PATH_MAC;
-        }
+        fullpath += wxT('[');
+    }
 
 
-        wxChar chPathSep = GetPathSeparators(format)[0u];
-        if ( format == wxPATH_VMS )
+    const size_t dirCount = m_dirs.GetCount();
+    for ( size_t i = 0; i < dirCount; i++ )
+    {
+        switch (format)
         {
         {
-            fullpath += _T('[');
-        }
+            case wxPATH_MAC:
+                if ( m_dirs[i] == wxT(".") )
+                {
+                    // skip appending ':', this shouldn't be done in this
+                    // case as "::" is interpreted as ".." under Unix
+                    continue;
+                }
 
 
-        for ( size_t i = 0; i < dirCount; i++ )
-        {
-            // under VMS, we shouldn't have a leading dot
-            if ( i && (format != wxPATH_VMS || !m_dirs[i - 1].empty()) )
-                fullpath += chPathSep;
+                // convert back from ".." to nothing
+                if ( !m_dirs[i].IsSameAs(wxT("..")) )
+                     fullpath += m_dirs[i];
+                break;
 
 
-            fullpath += m_dirs[i];
-        }
+            default:
+                wxFAIL_MSG( wxT("Unexpected path format") );
+                // still fall through
 
 
-        if ( format == wxPATH_VMS )
-        {
-            fullpath += _T(']');
-        }
-        else // !VMS
-        {
-            // separate the file name from the last directory, notice that we
-            // intentionally do it even if the name and extension are empty as
-            // this allows us to distinguish the directories from the file
-            // names (the directories have the trailing slash)
-            fullpath += chPathSep;
+            case wxPATH_DOS:
+            case wxPATH_UNIX:
+                fullpath += m_dirs[i];
+                break;
+
+            case wxPATH_VMS:
+                // TODO: What to do with ".." under VMS
+
+                // convert back from ".." to nothing
+                if ( !m_dirs[i].IsSameAs(wxT("..")) )
+                    fullpath += m_dirs[i];
+                break;
         }
         }
+
+        if ( (flags & wxPATH_GET_SEPARATOR) || (i != dirCount - 1) )
+            fullpath += GetPathSeparator(format);
+    }
+
+    if ( format == wxPATH_VMS )
+    {
+        fullpath += wxT(']');
     }
 
     }
 
-    // finally add the file name and extension
+    return fullpath;
+}
+
+wxString wxFileName::GetFullPath( wxPathFormat format ) const
+{
+    // we already have a function to get the path
+    wxString fullpath = GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR,
+                                format);
+
+    // now just add the file name and extension to it
     fullpath += GetFullName();
 
     return fullpath;
     fullpath += GetFullName();
 
     return fullpath;
@@ -1082,28 +2057,26 @@ wxString wxFileName::GetFullPath( wxPathFormat format ) const
 // Return the short form of the path (returns identity on non-Windows platforms)
 wxString wxFileName::GetShortPath() const
 {
 // Return the short form of the path (returns identity on non-Windows platforms)
 wxString wxFileName::GetShortPath() const
 {
-#if defined(__WXMSW__) && defined(__WIN32__) && !defined(__WXMICROWIN__)
     wxString path(GetFullPath());
     wxString path(GetFullPath());
-    wxString pathOut;
-    DWORD sz = ::GetShortPathName(path, NULL, 0);
-    bool ok = sz != 0;
-    if ( ok )
+
+#if defined(__WXMSW__) && defined(__WIN32__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
+    DWORD sz = ::GetShortPathName(path.t_str(), NULL, 0);
+    if ( sz != 0 )
     {
     {
-        ok = ::GetShortPathName
+        wxString pathOut;
+        if ( ::GetShortPathName
                (
                (
-                path,
-                pathOut.GetWriteBuf(sz),
+                path.t_str(),
+                wxStringBuffer(pathOut, sz),
                 sz
                 sz
-               ) != 0;
-        pathOut.UngetWriteBuf();
+               ) != 0 )
+        {
+            return pathOut;
+        }
     }
     }
-    if (ok)
-        return pathOut;
+#endif // Windows
 
     return path;
 
     return path;
-#else
-    return GetFullPath();
-#endif
 }
 
 // Return the long form of the path (returns identity on non-Windows platforms)
 }
 
 // Return the long form of the path (returns identity on non-Windows platforms)
@@ -1112,107 +2085,118 @@ wxString wxFileName::GetLongPath() const
     wxString pathOut,
              path = GetFullPath();
 
     wxString pathOut,
              path = GetFullPath();
 
-#if defined(__WIN32__) && !defined(__WXMICROWIN__)
-    bool success = FALSE;
-
-    // VZ: this code was disabled, why?
-#if 0 // wxUSE_DYNLIB_CLASS
-    typedef DWORD (*GET_LONG_PATH_NAME)(const wxChar *, wxChar *, DWORD);
+#if defined(__WIN32__) && !defined(__WXWINCE__) && !defined(__WXMICROWIN__)
 
 
-    static bool s_triedToLoad = FALSE;
+#if wxUSE_DYNLIB_CLASS
+    typedef DWORD (WINAPI *GET_LONG_PATH_NAME)(const wxChar *, wxChar *, DWORD);
 
 
-    if ( !s_triedToLoad )
+    // this is MT-safe as in the worst case we're going to resolve the function
+    // twice -- but as the result is the same in both threads, it's ok
+    static GET_LONG_PATH_NAME s_pfnGetLongPathName = NULL;
+    if ( !s_pfnGetLongPathName )
     {
     {
-        s_triedToLoad = TRUE;
-        wxDllType dllKernel = wxDllLoader::LoadLibrary(_T("kernel32"));
-        if ( dllKernel )
+        static bool s_triedToLoad = false;
+
+        if ( !s_triedToLoad )
         {
         {
-            // may succeed or fail depending on the Windows version
-            static GET_LONG_PATH_NAME s_pfnGetLongPathName = NULL;
-#ifdef _UNICODE
-            s_pfnGetLongPathName = (GET_LONG_PATH_NAME) wxDllLoader::GetSymbol(dllKernel, _T("GetLongPathNameW"));
-#else
-            s_pfnGetLongPathName = (GET_LONG_PATH_NAME) wxDllLoader::GetSymbol(dllKernel, _T("GetLongPathNameA"));
-#endif
+            s_triedToLoad = true;
 
 
-            wxDllLoader::UnloadLibrary(dllKernel);
+            wxDynamicLibrary dllKernel(wxT("kernel32"));
 
 
-            if ( s_pfnGetLongPathName )
+            const wxChar* GetLongPathName = wxT("GetLongPathName")
+#if wxUSE_UNICODE
+                              wxT("W");
+#else // ANSI
+                              wxT("A");
+#endif // Unicode/ANSI
+
+            if ( dllKernel.HasSymbol(GetLongPathName) )
             {
             {
-                DWORD dwSize = (*s_pfnGetLongPathName)(path, NULL, 0);
-                bool ok = dwSize > 0;
+                s_pfnGetLongPathName = (GET_LONG_PATH_NAME)
+                    dllKernel.GetSymbol(GetLongPathName);
+            }
 
 
-                if ( ok )
-                {
-                    DWORD sz = (*s_pfnGetLongPathName)(path, NULL, 0);
-                    ok = sz != 0;
-                    if ( ok )
-                    {
-                        ok = (*s_pfnGetLongPathName)
-                                (
-                                path,
-                                pathOut.GetWriteBuf(sz),
-                                sz
-                                ) != 0;
-                        pathOut.UngetWriteBuf();
-
-                        success = TRUE;
-                    }
-                }
+            // note that kernel32.dll can be unloaded, it stays in memory
+            // anyhow as all Win32 programs link to it and so it's safe to call
+            // GetLongPathName() even after unloading it
+        }
+    }
+
+    if ( s_pfnGetLongPathName )
+    {
+        DWORD dwSize = (*s_pfnGetLongPathName)(path.t_str(), NULL, 0);
+        if ( dwSize > 0 )
+        {
+            if ( (*s_pfnGetLongPathName)
+                 (
+                  path.t_str(),
+                  wxStringBuffer(pathOut, dwSize),
+                  dwSize
+                 ) != 0 )
+            {
+                return pathOut;
             }
         }
     }
             }
         }
     }
-    if (success)
-        return pathOut;
 #endif // wxUSE_DYNLIB_CLASS
 
 #endif // wxUSE_DYNLIB_CLASS
 
-    if (!success)
-    {
-        // The OS didn't support GetLongPathName, or some other error.
-        // We need to call FindFirstFile on each component in turn.
+    // The OS didn't support GetLongPathName, or some other error.
+    // We need to call FindFirstFile on each component in turn.
+
+    WIN32_FIND_DATA findFileData;
+    HANDLE hFind;
 
 
-        WIN32_FIND_DATA findFileData;
-        HANDLE hFind;
+    if ( HasVolume() )
+        pathOut = GetVolume() +
+                  GetVolumeSeparator(wxPATH_DOS) +
+                  GetPathSeparator(wxPATH_DOS);
+    else
         pathOut = wxEmptyString;
 
         pathOut = wxEmptyString;
 
-        wxArrayString dirs = GetDirs();
-        dirs.Add(GetFullName());
+    wxArrayString dirs = GetDirs();
+    dirs.Add(GetFullName());
 
 
-        wxString tmpPath;
+    wxString tmpPath;
 
 
-        size_t count = dirs.GetCount();
-        for ( size_t i = 0; i < count; i++ )
+    size_t count = dirs.GetCount();
+    for ( size_t i = 0; i < count; i++ )
+    {
+        const wxString& dir = dirs[i];
+
+        // We're using pathOut to collect the long-name path, but using a
+        // temporary for appending the last path component which may be
+        // short-name
+        tmpPath = pathOut + dir;
+
+        // We must not process "." or ".." here as they would be (unexpectedly)
+        // replaced by the corresponding directory names so just leave them
+        // alone
+        //
+        // And we can't pass a drive and root dir to FindFirstFile (VZ: why?)
+        if ( tmpPath.empty() || dir == '.' || dir == ".." ||
+                tmpPath.Last() == GetVolumeSeparator(wxPATH_DOS) )
         {
         {
-            // We're using pathOut to collect the long-name path, but using a
-            // temporary for appending the last path component which may be
-            // short-name
-            tmpPath = pathOut + dirs[i];
-
-            if ( tmpPath.empty() )
-                continue;
+            tmpPath += wxFILE_SEP_PATH;
+            pathOut = tmpPath;
+            continue;
+        }
 
 
-            if ( tmpPath.Last() == wxT(':') )
-            {
-                // Can't pass a drive and root dir to FindFirstFile,
-                // so continue to next dir
-                tmpPath += wxFILE_SEP_PATH;
-                pathOut = tmpPath;
-                continue;
-            }
+        hFind = ::FindFirstFile(tmpPath.t_str(), &findFileData);
+        if (hFind == INVALID_HANDLE_VALUE)
+        {
+            // Error: most likely reason is that path doesn't exist, so
+            // append any unprocessed parts and return
+            for ( i += 1; i < count; i++ )
+                tmpPath += wxFILE_SEP_PATH + dirs[i];
 
 
-            hFind = ::FindFirstFile(tmpPath, &findFileData);
-            if (hFind == INVALID_HANDLE_VALUE)
-            {
-                // Error: return immediately with the original path
-                return path;
-            }
+            return tmpPath;
+        }
 
 
-            pathOut += findFileData.cFileName;
-            if ( (i < (count-1)) )
-                pathOut += wxFILE_SEP_PATH;
+        pathOut += findFileData.cFileName;
+        if ( (i < (count-1)) )
+            pathOut += wxFILE_SEP_PATH;
 
 
-            ::FindClose(hFind);
-        }
+        ::FindClose(hFind);
     }
 #else // !Win32
     pathOut = path;
     }
 #else // !Win32
     pathOut = path;
@@ -1225,10 +2209,8 @@ wxPathFormat wxFileName::GetFormat( wxPathFormat format )
 {
     if (format == wxPATH_NATIVE)
     {
 {
     if (format == wxPATH_NATIVE)
     {
-#if defined(__WXMSW__) || defined(__WXPM__) || defined(__DOS__)
+#if defined(__WXMSW__) || defined(__OS2__) || defined(__DOS__)
         format = wxPATH_DOS;
         format = wxPATH_DOS;
-#elif defined(__WXMAC__) && !defined(__DARWIN__)
-        format = wxPATH_MAC;
 #elif defined(__VMS)
         format = wxPATH_VMS;
 #else
 #elif defined(__VMS)
         format = wxPATH_VMS;
 #else
@@ -1238,44 +2220,68 @@ wxPathFormat wxFileName::GetFormat( wxPathFormat format )
     return format;
 }
 
     return format;
 }
 
+#ifdef wxHAS_FILESYSTEM_VOLUMES
+
+/* static */
+wxString wxFileName::GetVolumeString(char drive, int flags)
+{
+    wxASSERT_MSG( !(flags & ~wxPATH_GET_SEPARATOR), "invalid flag specified" );
+
+    wxString vol(drive);
+    vol += wxFILE_SEP_DSK;
+    if ( flags & wxPATH_GET_SEPARATOR )
+        vol += wxFILE_SEP_PATH;
+
+    return vol;
+}
+
+#endif // wxHAS_FILESYSTEM_VOLUMES
+
 // ----------------------------------------------------------------------------
 // path splitting function
 // ----------------------------------------------------------------------------
 
 /* static */
 // ----------------------------------------------------------------------------
 // path splitting function
 // ----------------------------------------------------------------------------
 
 /* static */
-void wxFileName::SplitPath(const wxString& fullpathWithVolume,
-                           wxString *pstrVolume,
-                           wxString *pstrPath,
-                           wxString *pstrName,
-                           wxString *pstrExt,
-                           wxPathFormat format)
+void
+wxFileName::SplitVolume(const wxString& fullpathWithVolume,
+                        wxString *pstrVolume,
+                        wxString *pstrPath,
+                        wxPathFormat format)
 {
     format = GetFormat(format);
 
     wxString fullpath = fullpathWithVolume;
 
 {
     format = GetFormat(format);
 
     wxString fullpath = fullpathWithVolume;
 
-    // under VMS the end of the path is ']', not the path separator used to
-    // separate the components
-    wxString sepPath = format == wxPATH_VMS ? wxString(_T(']'))
-                                            : GetPathSeparators(format);
+    if ( IsMSWUniqueVolumeNamePath(fullpath, format) )
+    {
+        // special Windows unique volume names hack: transform
+        // \\?\Volume{guid}\path into Volume{guid}:path
+        // note: this check must be done before the check for UNC path
+
+        // we know the last backslash from the unique volume name is located
+        // there from IsMSWUniqueVolumeNamePath
+        fullpath[wxMSWUniqueVolumePrefixLength - 1] = wxFILE_SEP_DSK;
+
+        // paths starting with a unique volume name should always be absolute
+        fullpath.insert(wxMSWUniqueVolumePrefixLength, 1, wxFILE_SEP_PATH_DOS);
 
 
-    // special Windows UNC paths hack: transform \\share\path into share:path
-    if ( format == wxPATH_DOS )
+        // remove the leading "\\?\" part
+        fullpath.erase(0, 4);
+    }
+    else if ( IsUNCPath(fullpath, format) )
     {
     {
-        if ( fullpath.length() >= 4 &&
-                fullpath[0u] == wxFILE_SEP_PATH_DOS &&
-                    fullpath[1u] == wxFILE_SEP_PATH_DOS )
-        {
-            fullpath.erase(0, 2);
+        // special Windows UNC paths hack: transform \\share\path into share:path
 
 
-            size_t posFirstSlash = fullpath.find_first_of(sepPath);
-            if ( posFirstSlash != wxString::npos )
-            {
-                fullpath[posFirstSlash] = wxFILE_SEP_DSK;
+        fullpath.erase(0, 2);
 
 
-                // UNC paths are always absolute, right? (FIXME)
-                fullpath.insert(posFirstSlash + 1, wxFILE_SEP_PATH_DOS);
-            }
+        size_t posFirstSlash =
+            fullpath.find_first_of(GetPathTerminators(format));
+        if ( posFirstSlash != wxString::npos )
+        {
+            fullpath[posFirstSlash] = wxFILE_SEP_DSK;
+
+            // UNC paths are always absolute, right? (FIXME)
+            fullpath.insert(posFirstSlash + 1, 1, wxFILE_SEP_PATH_DOS);
         }
     }
 
         }
     }
 
@@ -1284,8 +2290,11 @@ void wxFileName::SplitPath(const wxString& fullpathWithVolume,
     {
         wxString sepVol = GetVolumeSeparator(format);
 
     {
         wxString sepVol = GetVolumeSeparator(format);
 
+        // we have to exclude the case of a colon in the very beginning of the
+        // string as it can't be a volume separator (nor can this be a valid
+        // DOS file name at all but we'll leave dealing with this to our caller)
         size_t posFirstColon = fullpath.find_first_of(sepVol);
         size_t posFirstColon = fullpath.find_first_of(sepVol);
-        if ( posFirstColon != wxString::npos )
+        if ( posFirstColon && posFirstColon != wxString::npos )
         {
             if ( pstrVolume )
             {
         {
             if ( pstrVolume )
             {
@@ -1297,21 +2306,38 @@ void wxFileName::SplitPath(const wxString& fullpathWithVolume,
         }
     }
 
         }
     }
 
+    if ( pstrPath )
+        *pstrPath = fullpath;
+}
+
+/* static */
+void wxFileName::SplitPath(const wxString& fullpathWithVolume,
+                           wxString *pstrVolume,
+                           wxString *pstrPath,
+                           wxString *pstrName,
+                           wxString *pstrExt,
+                           bool *hasExt,
+                           wxPathFormat format)
+{
+    format = GetFormat(format);
+
+    wxString fullpath;
+    SplitVolume(fullpathWithVolume, pstrVolume, &fullpath, format);
+
     // find the positions of the last dot and last path separator in the path
     size_t posLastDot = fullpath.find_last_of(wxFILE_SEP_EXT);
     // find the positions of the last dot and last path separator in the path
     size_t posLastDot = fullpath.find_last_of(wxFILE_SEP_EXT);
-    size_t posLastSlash = fullpath.find_last_of(sepPath);
+    size_t posLastSlash = fullpath.find_last_of(GetPathTerminators(format));
 
 
+    // check whether this dot occurs at the very beginning of a path component
     if ( (posLastDot != wxString::npos) &&
     if ( (posLastDot != wxString::npos) &&
-            ((format == wxPATH_UNIX) || (format == wxPATH_VMS)) )
+         (posLastDot == 0 ||
+            IsPathSeparator(fullpath[posLastDot - 1]) ||
+            (format == wxPATH_VMS && fullpath[posLastDot - 1] == wxT(']'))) )
     {
     {
-        if ( (posLastDot == 0) ||
-             (fullpath[posLastDot - 1] == sepPath[0u] ) )
-        {
-            // under Unix and VMS, dot may be (and commonly is) the first
-            // character of the filename, don't treat the entire filename as
-            // extension in this case
-            posLastDot = wxString::npos;
-        }
+        // dot may be (and commonly -- at least under Unix -- is) the first
+        // character of the filename, don't treat the entire filename as
+        // extension in this case
+        posLastDot = wxString::npos;
     }
 
     // if we do have a dot and a slash, check that the dot is in the name part
     }
 
     // if we do have a dot and a slash, check that the dot is in the name part
@@ -1334,10 +2360,13 @@ void wxFileName::SplitPath(const wxString& fullpathWithVolume,
         else
         {
             // take everything up to the path separator but take care to make
         else
         {
             // take everything up to the path separator but take care to make
-            // tha path equal to something like '/', not empty, for the files
+            // the path equal to something like '/', not empty, for the files
             // immediately under root directory
             size_t len = posLastSlash;
             // immediately under root directory
             size_t len = posLastSlash;
-            if ( !len )
+
+            // this rule does not apply to mac since we do not start with colons (sep)
+            // except for relative paths
+            if ( !len && format != wxPATH_MAC)
                 len++;
 
             *pstrPath = fullpath.Left(len);
                 len++;
 
             *pstrPath = fullpath.Left(len);
@@ -1345,7 +2374,7 @@ void wxFileName::SplitPath(const wxString& fullpathWithVolume,
             // special VMS hack: remove the initial bracket
             if ( format == wxPATH_VMS )
             {
             // special VMS hack: remove the initial bracket
             if ( format == wxPATH_VMS )
             {
-                if ( (*pstrPath)[0u] == _T('[') )
+                if ( (*pstrPath)[0u] == wxT('[') )
                     pstrPath->erase(0, 1);
             }
         }
                     pstrPath->erase(0, 1);
             }
         }
@@ -1374,18 +2403,25 @@ void wxFileName::SplitPath(const wxString& fullpathWithVolume,
         *pstrName = fullpath.Mid(nStart, count);
     }
 
         *pstrName = fullpath.Mid(nStart, count);
     }
 
-    if ( pstrExt )
+    // finally deal with the extension here: we have an added complication that
+    // extension may be empty (but present) as in "foo." where trailing dot
+    // indicates the empty extension at the end -- and hence we must remember
+    // that we have it independently of pstrExt
+    if ( posLastDot == wxString::npos )
     {
     {
-        if ( posLastDot == wxString::npos )
-        {
-            // no extension
-            pstrExt->Empty();
-        }
-        else
-        {
-            // take everything after the dot
+        // no extension
+        if ( pstrExt )
+            pstrExt->clear();
+        if ( hasExt )
+            *hasExt = false;
+    }
+    else
+    {
+        // take everything after the dot
+        if ( pstrExt )
             *pstrExt = fullpath.Mid(posLastDot + 1);
             *pstrExt = fullpath.Mid(posLastDot + 1);
-        }
+        if ( hasExt )
+            *hasExt = true;
     }
 }
 
     }
 }
 
@@ -1399,129 +2435,432 @@ void wxFileName::SplitPath(const wxString& fullpath,
     wxString volume;
     SplitPath(fullpath, &volume, path, name, ext, format);
 
     wxString volume;
     SplitPath(fullpath, &volume, path, name, ext, format);
 
-    if ( path && !volume.empty() )
+    if ( path )
     {
     {
-        path->Prepend(volume + GetVolumeSeparator(format));
+        path->Prepend(wxGetVolumeString(volume, format));
     }
 }
 
     }
 }
 
+/* static */
+wxString wxFileName::StripExtension(const wxString& fullpath)
+{
+    wxFileName fn(fullpath);
+    fn.SetExt("");
+    return fn.GetFullPath();
+}
+
 // ----------------------------------------------------------------------------
 // time functions
 // ----------------------------------------------------------------------------
 
 // ----------------------------------------------------------------------------
 // time functions
 // ----------------------------------------------------------------------------
 
-bool wxFileName::SetTimes(const wxDateTime *dtCreate,
-                          const wxDateTime *dtAccess,
-                          const wxDateTime *dtMod)
+#if wxUSE_DATETIME
+
+bool wxFileName::SetTimes(const wxDateTime *dtAccess,
+                          const wxDateTime *dtMod,
+                          const wxDateTime *dtCreate) const
 {
 {
-#if defined(__UNIX_LIKE__) || (defined(__DOS__) && defined(__WATCOMC__))
-    if ( !dtAccess && !dtMod )
+#if defined(__WIN32__)
+    FILETIME ftAccess, ftCreate, ftWrite;
+
+    if ( dtCreate )
+        ConvertWxToFileTime(&ftCreate, *dtCreate);
+    if ( dtAccess )
+        ConvertWxToFileTime(&ftAccess, *dtAccess);
+    if ( dtMod )
+        ConvertWxToFileTime(&ftWrite, *dtMod);
+
+    wxString path;
+    int flags;
+    if ( IsDir() )
     {
     {
-        // can't modify the creation time anyhow, don't try
-        return TRUE;
-    }
+        if ( wxGetOsVersion() == wxOS_WINDOWS_9X )
+        {
+            wxLogError(_("Setting directory access times is not supported "
+                         "under this OS version"));
+            return false;
+        }
 
 
-    // if dtAccess or dtMod is not specified, use the other one (which must be
-    // non NULL because of the test above) for both times
-    utimbuf utm;
-    utm.actime = dtAccess ? dtAccess->GetTicks() : dtMod->GetTicks();
-    utm.modtime = dtMod ? dtMod->GetTicks() : dtAccess->GetTicks();
-    if ( utime(GetFullPath(), &utm) == 0 )
+        path = GetPath();
+        flags = FILE_FLAG_BACKUP_SEMANTICS;
+    }
+    else // file
     {
     {
-        return TRUE;
+        path = GetFullPath();
+        flags = 0;
     }
     }
-#elif defined(__WIN32__)
-    wxFileHandle fh(GetFullPath());
+
+    wxFileHandle fh(path, wxFileHandle::WriteAttr, flags);
     if ( fh.IsOk() )
     {
     if ( fh.IsOk() )
     {
-        FILETIME ftAccess, ftCreate, ftWrite;
-
-        if ( dtCreate )
-            ConvertWxToFileTime(&ftCreate, *dtCreate);
-        if ( dtAccess )
-            ConvertWxToFileTime(&ftAccess, *dtAccess);
-        if ( dtMod )
-            ConvertWxToFileTime(&ftWrite, *dtMod);
-
         if ( ::SetFileTime(fh,
                            dtCreate ? &ftCreate : NULL,
                            dtAccess ? &ftAccess : NULL,
                            dtMod ? &ftWrite : NULL) )
         {
         if ( ::SetFileTime(fh,
                            dtCreate ? &ftCreate : NULL,
                            dtAccess ? &ftAccess : NULL,
                            dtMod ? &ftWrite : NULL) )
         {
-            return TRUE;
+            return true;
         }
     }
         }
     }
+#elif defined(__UNIX_LIKE__) || (defined(__DOS__) && defined(__WATCOMC__))
+    wxUnusedVar(dtCreate);
+
+    if ( !dtAccess && !dtMod )
+    {
+        // can't modify the creation time anyhow, don't try
+        return true;
+    }
+
+    // if dtAccess or dtMod is not specified, use the other one (which must be
+    // non NULL because of the test above) for both times
+    utimbuf utm;
+    utm.actime = dtAccess ? dtAccess->GetTicks() : dtMod->GetTicks();
+    utm.modtime = dtMod ? dtMod->GetTicks() : dtAccess->GetTicks();
+    if ( utime(GetFullPath().fn_str(), &utm) == 0 )
+    {
+        return true;
+    }
 #else // other platform
 #else // other platform
+    wxUnusedVar(dtAccess);
+    wxUnusedVar(dtMod);
+    wxUnusedVar(dtCreate);
 #endif // platforms
 
     wxLogSysError(_("Failed to modify file times for '%s'"),
                   GetFullPath().c_str());
 
 #endif // platforms
 
     wxLogSysError(_("Failed to modify file times for '%s'"),
                   GetFullPath().c_str());
 
-    return FALSE;
+    return false;
 }
 
 }
 
-bool wxFileName::Touch()
+bool wxFileName::Touch() const
 {
 #if defined(__UNIX_LIKE__)
     // under Unix touching file is simple: just pass NULL to utime()
 {
 #if defined(__UNIX_LIKE__)
     // under Unix touching file is simple: just pass NULL to utime()
-    if ( utime(GetFullPath(), NULL) == 0 )
+    if ( utime(GetFullPath().fn_str(), NULL) == 0 )
     {
     {
-        return TRUE;
+        return true;
     }
 
     wxLogSysError(_("Failed to touch the file '%s'"), GetFullPath().c_str());
 
     }
 
     wxLogSysError(_("Failed to touch the file '%s'"), GetFullPath().c_str());
 
-    return FALSE;
+    return false;
 #else // other platform
     wxDateTime dtNow = wxDateTime::Now();
 
 #else // other platform
     wxDateTime dtNow = wxDateTime::Now();
 
-    return SetTimes(NULL /* don't change create time */, &dtNow, &dtNow);
+    return SetTimes(&dtNow, &dtNow, NULL /* don't change create time */);
 #endif // platforms
 }
 
 bool wxFileName::GetTimes(wxDateTime *dtAccess,
                           wxDateTime *dtMod,
 #endif // platforms
 }
 
 bool wxFileName::GetTimes(wxDateTime *dtAccess,
                           wxDateTime *dtMod,
-                          wxDateTime *dtChange) const
+                          wxDateTime *dtCreate) const
 {
 {
-#if defined(__UNIX_LIKE__) || defined(__WXMAC__) || (defined(__DOS__) && defined(__WATCOMC__))
+#if defined(__WIN32__)
+    // we must use different methods for the files and directories under
+    // Windows as CreateFile(GENERIC_READ) doesn't work for the directories and
+    // CreateFile(FILE_FLAG_BACKUP_SEMANTICS) works -- but only under NT and
+    // not 9x
+    bool ok;
+    FILETIME ftAccess, ftCreate, ftWrite;
+    if ( IsDir() )
+    {
+        // implemented in msw/dir.cpp
+        extern bool wxGetDirectoryTimes(const wxString& dirname,
+                                        FILETIME *, FILETIME *, FILETIME *);
+
+        // we should pass the path without the trailing separator to
+        // wxGetDirectoryTimes()
+        ok = wxGetDirectoryTimes(GetPath(wxPATH_GET_VOLUME),
+                                 &ftAccess, &ftCreate, &ftWrite);
+    }
+    else // file
+    {
+        wxFileHandle fh(GetFullPath(), wxFileHandle::ReadAttr);
+        if ( fh.IsOk() )
+        {
+            ok = ::GetFileTime(fh,
+                               dtCreate ? &ftCreate : NULL,
+                               dtAccess ? &ftAccess : NULL,
+                               dtMod ? &ftWrite : NULL) != 0;
+        }
+        else
+        {
+            ok = false;
+        }
+    }
+
+    if ( ok )
+    {
+        if ( dtCreate )
+            ConvertFileTimeToWx(dtCreate, ftCreate);
+        if ( dtAccess )
+            ConvertFileTimeToWx(dtAccess, ftAccess);
+        if ( dtMod )
+            ConvertFileTimeToWx(dtMod, ftWrite);
+
+        return true;
+    }
+#elif defined(__UNIX_LIKE__) || defined(__WXMAC__) || defined(__OS2__) || (defined(__DOS__) && defined(__WATCOMC__))
+    // no need to test for IsDir() here
     wxStructStat stBuf;
     wxStructStat stBuf;
-    if ( wxStat(GetFullPath(), &stBuf) == 0 )
+    if ( wxStat( GetFullPath(), &stBuf) == 0 )
     {
         if ( dtAccess )
             dtAccess->Set(stBuf.st_atime);
         if ( dtMod )
             dtMod->Set(stBuf.st_mtime);
     {
         if ( dtAccess )
             dtAccess->Set(stBuf.st_atime);
         if ( dtMod )
             dtMod->Set(stBuf.st_mtime);
-        if ( dtChange )
-            dtChange->Set(stBuf.st_ctime);
+        if ( dtCreate )
+            dtCreate->Set(stBuf.st_ctime);
 
 
-        return TRUE;
+        return true;
     }
     }
-#elif defined(__WIN32__)
-    wxFileHandle fh(GetFullPath());
-    if ( fh.IsOk() )
+#else // other platform
+    wxUnusedVar(dtAccess);
+    wxUnusedVar(dtMod);
+    wxUnusedVar(dtCreate);
+#endif // platforms
+
+    wxLogSysError(_("Failed to retrieve file times for '%s'"),
+                  GetFullPath().c_str());
+
+    return false;
+}
+
+#endif // wxUSE_DATETIME
+
+
+// ----------------------------------------------------------------------------
+// file size functions
+// ----------------------------------------------------------------------------
+
+#if wxUSE_LONGLONG
+
+/* static */
+wxULongLong wxFileName::GetSize(const wxString &filename)
+{
+    if (!wxFileExists(filename))
+        return wxInvalidSize;
+
+#if defined(__WIN32__)
+    wxFileHandle f(filename, wxFileHandle::ReadAttr);
+    if (!f.IsOk())
+        return wxInvalidSize;
+
+    DWORD lpFileSizeHigh;
+    DWORD ret = GetFileSize(f, &lpFileSizeHigh);
+    if ( ret == INVALID_FILE_SIZE && ::GetLastError() != NO_ERROR )
+        return wxInvalidSize;
+
+    return wxULongLong(lpFileSizeHigh, ret);
+#else // ! __WIN32__
+    wxStructStat st;
+    if (wxStat( filename, &st) != 0)
+        return wxInvalidSize;
+    return wxULongLong(st.st_size);
+#endif
+}
+
+/* static */
+wxString wxFileName::GetHumanReadableSize(const wxULongLong &bs,
+                                          const wxString &nullsize,
+                                          int precision,
+                                          wxSizeConvention conv)
+{
+    // deal with trivial case first
+    if ( bs == 0 || bs == wxInvalidSize )
+        return nullsize;
+
+    // depending on the convention used the multiplier may be either 1000 or
+    // 1024 and the binary infix may be empty (for "KB") or "i" (for "KiB")
+    double multiplier = 1024.;
+    wxString biInfix;
+
+    switch ( conv )
     {
     {
-        FILETIME ftAccess, ftCreate, ftWrite;
+        case wxSIZE_CONV_TRADITIONAL:
+            // nothing to do, this corresponds to the default values of both
+            // the multiplier and infix string
+            break;
 
 
-        if ( ::GetFileTime(fh,
-                           dtMod ? &ftCreate : NULL,
-                           dtAccess ? &ftAccess : NULL,
-                           dtChange ? &ftWrite : NULL) )
+        case wxSIZE_CONV_IEC:
+            biInfix = "i";
+            break;
+
+        case wxSIZE_CONV_SI:
+            multiplier = 1000;
+            break;
+    }
+
+    const double kiloByteSize = multiplier;
+    const double megaByteSize = multiplier * kiloByteSize;
+    const double gigaByteSize = multiplier * megaByteSize;
+    const double teraByteSize = multiplier * gigaByteSize;
+
+    const double bytesize = bs.ToDouble();
+
+    wxString result;
+    if ( bytesize < kiloByteSize )
+        result.Printf("%s B", bs.ToString());
+    else if ( bytesize < megaByteSize )
+        result.Printf("%.*f K%sB", precision, bytesize/kiloByteSize, biInfix);
+    else if (bytesize < gigaByteSize)
+        result.Printf("%.*f M%sB", precision, bytesize/megaByteSize, biInfix);
+    else if (bytesize < teraByteSize)
+        result.Printf("%.*f G%sB", precision, bytesize/gigaByteSize, biInfix);
+    else
+        result.Printf("%.*f T%sB", precision, bytesize/teraByteSize, biInfix);
+
+    return result;
+}
+
+wxULongLong wxFileName::GetSize() const
+{
+    return GetSize(GetFullPath());
+}
+
+wxString wxFileName::GetHumanReadableSize(const wxString& failmsg,
+                                          int precision,
+                                          wxSizeConvention conv) const
+{
+    return GetHumanReadableSize(GetSize(), failmsg, precision, conv);
+}
+
+#endif // wxUSE_LONGLONG
+
+// ----------------------------------------------------------------------------
+// Mac-specific functions
+// ----------------------------------------------------------------------------
+
+#if defined( __WXOSX_MAC__ ) && wxOSX_USE_CARBON
+
+namespace
+{
+
+class MacDefaultExtensionRecord
+{
+public:
+    MacDefaultExtensionRecord()
+    {
+        m_type =
+        m_creator = 0 ;
+    }
+
+    // default copy ctor, assignment operator and dtor are ok
+
+    MacDefaultExtensionRecord(const wxString& ext, OSType type, OSType creator)
+        : m_ext(ext)
+    {
+        m_type = type;
+        m_creator = creator;
+    }
+
+    wxString m_ext;
+    OSType m_type;
+    OSType m_creator;
+};
+
+WX_DECLARE_OBJARRAY(MacDefaultExtensionRecord, MacDefaultExtensionArray);
+
+bool gMacDefaultExtensionsInited = false;
+
+#include "wx/arrimpl.cpp"
+
+WX_DEFINE_EXPORTED_OBJARRAY(MacDefaultExtensionArray);
+
+MacDefaultExtensionArray gMacDefaultExtensions;
+
+// load the default extensions
+const MacDefaultExtensionRecord gDefaults[] =
+{
+    MacDefaultExtensionRecord( "txt", 'TEXT', 'ttxt' ),
+    MacDefaultExtensionRecord( "tif", 'TIFF', '****' ),
+    MacDefaultExtensionRecord( "jpg", 'JPEG', '****' ),
+};
+
+void MacEnsureDefaultExtensionsLoaded()
+{
+    if ( !gMacDefaultExtensionsInited )
+    {
+        // we could load the pc exchange prefs here too
+        for ( size_t i = 0 ; i < WXSIZEOF( gDefaults ) ; ++i )
         {
         {
-            if ( dtMod )
-                ConvertFileTimeToWx(dtMod, ftCreate);
-            if ( dtAccess )
-                ConvertFileTimeToWx(dtAccess, ftAccess);
-            if ( dtChange )
-                ConvertFileTimeToWx(dtChange, ftWrite);
-
-            return TRUE;
+            gMacDefaultExtensions.Add( gDefaults[i] ) ;
         }
         }
+        gMacDefaultExtensionsInited = true;
     }
     }
-#else // other platform
-#endif // platforms
+}
 
 
-    wxLogSysError(_("Failed to retrieve file times for '%s'"),
-                  GetFullPath().c_str());
+} // anonymous namespace
+
+bool wxFileName::MacSetTypeAndCreator( wxUint32 type , wxUint32 creator )
+{
+    FSRef fsRef ;
+    FSCatalogInfo catInfo;
+    FileInfo *finfo ;
+
+    if ( wxMacPathToFSRef( GetFullPath() , &fsRef ) == noErr )
+    {
+        if ( FSGetCatalogInfo (&fsRef, kFSCatInfoFinderInfo, &catInfo, NULL, NULL, NULL) == noErr )
+        {
+            finfo = (FileInfo*)&catInfo.finderInfo;
+            finfo->fileType = type ;
+            finfo->fileCreator = creator ;
+            FSSetCatalogInfo( &fsRef, kFSCatInfoFinderInfo, &catInfo ) ;
+            return true ;
+        }
+    }
+    return false ;
+}
+
+bool wxFileName::MacGetTypeAndCreator( wxUint32 *type , wxUint32 *creator ) const
+{
+    FSRef fsRef ;
+    FSCatalogInfo catInfo;
+    FileInfo *finfo ;
+
+    if ( wxMacPathToFSRef( GetFullPath() , &fsRef ) == noErr )
+    {
+        if ( FSGetCatalogInfo (&fsRef, kFSCatInfoFinderInfo, &catInfo, NULL, NULL, NULL) == noErr )
+        {
+            finfo = (FileInfo*)&catInfo.finderInfo;
+            *type = finfo->fileType ;
+            *creator = finfo->fileCreator ;
+            return true ;
+        }
+    }
+    return false ;
+}
+
+bool wxFileName::MacSetDefaultTypeAndCreator()
+{
+    wxUint32 type , creator ;
+    if ( wxFileName::MacFindDefaultTypeAndCreator(GetExt() , &type ,
+      &creator ) )
+    {
+        return MacSetTypeAndCreator( type , creator ) ;
+    }
+    return false;
+}
+
+bool wxFileName::MacFindDefaultTypeAndCreator( const wxString& ext , wxUint32 *type , wxUint32 *creator )
+{
+  MacEnsureDefaultExtensionsLoaded() ;
+  wxString extl = ext.Lower() ;
+  for( int i = gMacDefaultExtensions.Count() - 1 ; i >= 0 ; --i )
+  {
+    if ( gMacDefaultExtensions.Item(i).m_ext == extl )
+    {
+      *type = gMacDefaultExtensions.Item(i).m_type ;
+      *creator = gMacDefaultExtensions.Item(i).m_creator ;
+      return true ;
+    }
+  }
+  return false ;
+}
 
 
-    return FALSE;
+void wxFileName::MacRegisterDefaultTypeAndCreator( const wxString& ext , wxUint32 type , wxUint32 creator )
+{
+  MacEnsureDefaultExtensionsLoaded();
+  MacDefaultExtensionRecord rec(ext.Lower(), type, creator);
+  gMacDefaultExtensions.Add( rec );
 }
 
 }
 
+#endif // defined( __WXOSX_MAC__ ) && wxOSX_USE_CARBON