1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/common/filename.cpp 
   3 // Purpose:     wxFileName - encapsulates a file path 
   4 // Author:      Robert Roebling, Vadim Zeitlin 
   8 // Copyright:   (c) 2000 Robert Roebling 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  13    Here are brief descriptions of the filename formats supported by this class: 
  15    wxPATH_UNIX: standard Unix format, used under Darwin as well, absolute file 
  17                 /dir1/dir2/.../dirN/filename, "." and ".." stand for the 
  18                 current and parent directory respectively, "~" is parsed as the 
  19                 user HOME and "~username" as the HOME of that user 
  21    wxPATH_DOS:  DOS/Windows format, absolute file names have the form: 
  22                 drive:\dir1\dir2\...\dirN\filename.ext where drive is a single 
  23                 letter. "." and ".." as for Unix but no "~". 
  25                 There are also UNC names of the form \\share\fullpath 
  27    wxPATH_MAC:  Mac OS 8/9 and Mac OS X under CodeWarrior 7 format, absolute file 
  29                     volume:dir1:...:dirN:filename 
  30                 and the relative file names are either 
  31                     :dir1:...:dirN:filename 
  34                 (although :filename works as well). 
  35                 Since the volume is just part of the file path, it is not 
  36                 treated like a separate entity as it is done under DOS and 
  37                 VMS, it is just treated as another dir. 
  39    wxPATH_VMS:  VMS native format, absolute file names have the form 
  40                     <device>:[dir1.dir2.dir3]file.txt 
  42                     <device>:[000000.dir1.dir2.dir3]file.txt 
  44                 the <device> is the physical device (i.e. disk). 000000 is the 
  45                 root directory on the device which can be omitted. 
  47                 Note that VMS uses different separators unlike Unix: 
  48                  : always after the device. If the path does not contain : than 
  49                    the default (the device of the current directory) is assumed. 
  50                  [ start of directory specification 
  51                  . separator between directory and subdirectory 
  52                  ] between directory and file 
  55 // ============================================================================ 
  57 // ============================================================================ 
  59 // ---------------------------------------------------------------------------- 
  61 // ---------------------------------------------------------------------------- 
  63 // For compilers that support precompilation, includes "wx.h". 
  64 #include "wx/wxprec.h" 
  76 #include "wx/filename.h" 
  77 #include "wx/tokenzr.h" 
  78 #include "wx/config.h"          // for wxExpandEnvVars 
  81 #include "wx/dynlib.h" 
  83 // For GetShort/LongPathName 
  85 #include "wx/msw/wrapwin.h" 
  86 #if defined(__MINGW32__) 
  87 #include "wx/msw/gccpriv.h" 
  92 #include "wx/msw/private.h" 
  95 #if defined(__WXMAC__) 
  96   #include  "wx/mac/private.h"  // includes mac headers 
  99 // utime() is POSIX so should normally be available on all Unices 
 101 #include <sys/types.h> 
 103 #include <sys/stat.h> 
 113 #include <sys/types.h> 
 115 #include <sys/stat.h> 
 126 #include <sys/utime.h> 
 127 #include <sys/stat.h> 
 138 #define MAX_PATH _MAX_PATH 
 141 // ---------------------------------------------------------------------------- 
 143 // ---------------------------------------------------------------------------- 
 145 // small helper class which opens and closes the file - we use it just to get 
 146 // a file handle for the given file name to pass it to some Win32 API function 
 147 #if defined(__WIN32__) && !defined(__WXMICROWIN__) 
 158     wxFileHandle(const wxString
& filename
, OpenMode mode
) 
 160         m_hFile 
= ::CreateFile
 
 163                      mode 
== Read 
? GENERIC_READ    
// access mask 
 165                      FILE_SHARE_READ 
|              // sharing mode 
 166                      FILE_SHARE_WRITE
,              // (allow everything) 
 167                      NULL
,                          // no secutity attr 
 168                      OPEN_EXISTING
,                 // creation disposition 
 170                      NULL                           
// no template file 
 173         if ( m_hFile 
== INVALID_HANDLE_VALUE 
) 
 175             wxLogSysError(_("Failed to open '%s' for %s"), 
 177                           mode 
== Read 
? _("reading") : _("writing")); 
 183         if ( m_hFile 
!= INVALID_HANDLE_VALUE 
) 
 185             if ( !::CloseHandle(m_hFile
) ) 
 187                 wxLogSysError(_("Failed to close file handle")); 
 192     // return true only if the file could be opened successfully 
 193     bool IsOk() const { return m_hFile 
!= INVALID_HANDLE_VALUE
; } 
 196     operator HANDLE() const { return m_hFile
; } 
 204 // ---------------------------------------------------------------------------- 
 206 // ---------------------------------------------------------------------------- 
 208 #if wxUSE_DATETIME && defined(__WIN32__) && !defined(__WXMICROWIN__) 
 210 // convert between wxDateTime and FILETIME which is a 64-bit value representing 
 211 // the number of 100-nanosecond intervals since January 1, 1601. 
 213 static void ConvertFileTimeToWx(wxDateTime 
*dt
, const FILETIME 
&ft
) 
 215     FILETIME ftcopy 
= ft
; 
 217     if ( !::FileTimeToLocalFileTime(&ftcopy
, &ftLocal
) ) 
 219         wxLogLastError(_T("FileTimeToLocalFileTime")); 
 223     if ( !::FileTimeToSystemTime(&ftLocal
, &st
) ) 
 225         wxLogLastError(_T("FileTimeToSystemTime")); 
 228     dt
->Set(st
.wDay
, wxDateTime::Month(st
.wMonth 
- 1), st
.wYear
, 
 229             st
.wHour
, st
.wMinute
, st
.wSecond
, st
.wMilliseconds
); 
 232 static void ConvertWxToFileTime(FILETIME 
*ft
, const wxDateTime
& dt
) 
 235     st
.wDay 
= dt
.GetDay(); 
 236     st
.wMonth 
= (WORD
)(dt
.GetMonth() + 1); 
 237     st
.wYear 
= (WORD
)dt
.GetYear(); 
 238     st
.wHour 
= dt
.GetHour(); 
 239     st
.wMinute 
= dt
.GetMinute(); 
 240     st
.wSecond 
= dt
.GetSecond(); 
 241     st
.wMilliseconds 
= dt
.GetMillisecond(); 
 244     if ( !::SystemTimeToFileTime(&st
, &ftLocal
) ) 
 246         wxLogLastError(_T("SystemTimeToFileTime")); 
 249     if ( !::LocalFileTimeToFileTime(&ftLocal
, ft
) ) 
 251         wxLogLastError(_T("LocalFileTimeToFileTime")); 
 255 #endif // wxUSE_DATETIME && __WIN32__ 
 257 // return a string with the volume par 
 258 static wxString 
wxGetVolumeString(const wxString
& volume
, wxPathFormat format
) 
 262     if ( !volume
.empty() ) 
 264         format 
= wxFileName::GetFormat(format
); 
 266         // Special Windows UNC paths hack, part 2: undo what we did in 
 267         // SplitPath() and make an UNC path if we have a drive which is not a 
 268         // single letter (hopefully the network shares can't be one letter only 
 269         // although I didn't find any authoritative docs on this) 
 270         if ( format 
== wxPATH_DOS 
&& volume
.length() > 1 ) 
 272             path 
<< wxFILE_SEP_PATH_DOS 
<< wxFILE_SEP_PATH_DOS 
<< volume
; 
 274         else if  ( format 
== wxPATH_DOS 
|| format 
== wxPATH_VMS 
) 
 276             path 
<< volume 
<< wxFileName::GetVolumeSeparator(format
); 
 284 // ============================================================================ 
 286 // ============================================================================ 
 288 // ---------------------------------------------------------------------------- 
 289 // wxFileName construction 
 290 // ---------------------------------------------------------------------------- 
 292 void wxFileName::Assign( const wxFileName 
&filepath 
) 
 294     m_volume 
= filepath
.GetVolume(); 
 295     m_dirs 
= filepath
.GetDirs(); 
 296     m_name 
= filepath
.GetName(); 
 297     m_ext 
= filepath
.GetExt(); 
 298     m_relative 
= filepath
.m_relative
; 
 299     m_hasExt 
= filepath
.m_hasExt
; 
 302 void wxFileName::Assign(const wxString
& volume
, 
 303                         const wxString
& path
, 
 304                         const wxString
& name
, 
 307                         wxPathFormat format 
) 
 309     SetPath( path
, format 
); 
 318 void wxFileName::SetPath( const wxString
& pathOrig
, wxPathFormat format 
) 
 322     if ( pathOrig
.empty() ) 
 330     format 
= GetFormat( format 
); 
 332     // 0) deal with possible volume part first 
 335     SplitVolume(pathOrig
, &volume
, &path
, format
); 
 336     if ( !volume
.empty() ) 
 343     // 1) Determine if the path is relative or absolute. 
 344     wxChar leadingChar 
= path
[0u]; 
 349             m_relative 
= leadingChar 
== wxT(':'); 
 351             // We then remove a leading ":". The reason is in our 
 352             // storage form for relative paths: 
 353             // ":dir:file.txt" actually means "./dir/file.txt" in 
 354             // DOS notation and should get stored as 
 355             // (relative) (dir) (file.txt) 
 356             // "::dir:file.txt" actually means "../dir/file.txt" 
 357             // stored as (relative) (..) (dir) (file.txt) 
 358             // This is important only for the Mac as an empty dir 
 359             // actually means <UP>, whereas under DOS, double 
 360             // slashes can be ignored: "\\\\" is the same as "\\". 
 366             // TODO: what is the relative path format here? 
 371             wxFAIL_MSG( _T("Unknown path format") ); 
 372             // !! Fall through !! 
 375             // the paths of the form "~" or "~username" are absolute 
 376             m_relative 
= leadingChar 
!= wxT('/') && leadingChar 
!= _T('~'); 
 380             m_relative 
= !IsPathSeparator(leadingChar
, format
); 
 385     // 2) Break up the path into its members. If the original path 
 386     //    was just "/" or "\\", m_dirs will be empty. We know from 
 387     //    the m_relative field, if this means "nothing" or "root dir". 
 389     wxStringTokenizer 
tn( path
, GetPathSeparators(format
) ); 
 391     while ( tn
.HasMoreTokens() ) 
 393         wxString token 
= tn
.GetNextToken(); 
 395         // Remove empty token under DOS and Unix, interpret them 
 399             if (format 
== wxPATH_MAC
) 
 400                 m_dirs
.Add( wxT("..") ); 
 410 void wxFileName::Assign(const wxString
& fullpath
, 
 413     wxString volume
, path
, name
, ext
; 
 415     SplitPath(fullpath
, &volume
, &path
, &name
, &ext
, &hasExt
, format
); 
 417     Assign(volume
, path
, name
, ext
, hasExt
, format
); 
 420 void wxFileName::Assign(const wxString
& fullpathOrig
, 
 421                         const wxString
& fullname
, 
 424     // always recognize fullpath as directory, even if it doesn't end with a 
 426     wxString fullpath 
= fullpathOrig
; 
 427     if ( !wxEndsWithPathSeparator(fullpath
) ) 
 429         fullpath 
+= GetPathSeparator(format
); 
 432     wxString volume
, path
, name
, ext
; 
 435     // do some consistency checks in debug mode: the name should be really just 
 436     // the filename and the path should be really just a path 
 438     wxString volDummy
, pathDummy
, nameDummy
, extDummy
; 
 440     SplitPath(fullname
, &volDummy
, &pathDummy
, &name
, &ext
, &hasExt
, format
); 
 442     wxASSERT_MSG( volDummy
.empty() && pathDummy
.empty(), 
 443                   _T("the file name shouldn't contain the path") ); 
 445     SplitPath(fullpath
, &volume
, &path
, &nameDummy
, &extDummy
, format
); 
 447     wxASSERT_MSG( nameDummy
.empty() && extDummy
.empty(), 
 448                   _T("the path shouldn't contain file name nor extension") ); 
 450 #else // !__WXDEBUG__ 
 451     SplitPath(fullname
, NULL 
/* no volume */, NULL 
/* no path */, 
 452                         &name
, &ext
, &hasExt
, format
); 
 453     SplitPath(fullpath
, &volume
, &path
, NULL
, NULL
, format
); 
 454 #endif // __WXDEBUG__/!__WXDEBUG__ 
 456     Assign(volume
, path
, name
, ext
, hasExt
, format
); 
 459 void wxFileName::Assign(const wxString
& pathOrig
, 
 460                         const wxString
& name
, 
 466     SplitVolume(pathOrig
, &volume
, &path
, format
); 
 468     Assign(volume
, path
, name
, ext
, format
); 
 471 void wxFileName::AssignDir(const wxString
& dir
, wxPathFormat format
) 
 473     Assign(dir
, wxEmptyString
, format
); 
 476 void wxFileName::Clear() 
 482     m_ext 
= wxEmptyString
; 
 484     // we don't have any absolute path for now 
 492 wxFileName 
wxFileName::FileName(const wxString
& file
, wxPathFormat format
) 
 494     return wxFileName(file
, format
); 
 498 wxFileName 
wxFileName::DirName(const wxString
& dir
, wxPathFormat format
) 
 501     fn
.AssignDir(dir
, format
); 
 505 // ---------------------------------------------------------------------------- 
 507 // ---------------------------------------------------------------------------- 
 509 bool wxFileName::FileExists() const 
 511     return wxFileName::FileExists( GetFullPath() ); 
 514 bool wxFileName::FileExists( const wxString 
&file 
) 
 516     return ::wxFileExists( file 
); 
 519 bool wxFileName::DirExists() const 
 521     return wxFileName::DirExists( GetFullPath() ); 
 524 bool wxFileName::DirExists( const wxString 
&dir 
) 
 526     return ::wxDirExists( dir 
); 
 529 // ---------------------------------------------------------------------------- 
 530 // CWD and HOME stuff 
 531 // ---------------------------------------------------------------------------- 
 533 void wxFileName::AssignCwd(const wxString
& volume
) 
 535     AssignDir(wxFileName::GetCwd(volume
)); 
 539 wxString 
wxFileName::GetCwd(const wxString
& volume
) 
 541     // if we have the volume, we must get the current directory on this drive 
 542     // and to do this we have to chdir to this volume - at least under Windows, 
 543     // I don't know how to get the current drive on another volume elsewhere 
 546     if ( !volume
.empty() ) 
 549         SetCwd(volume 
+ GetVolumeSeparator()); 
 552     wxString cwd 
= ::wxGetCwd(); 
 554     if ( !volume
.empty() ) 
 562 bool wxFileName::SetCwd() 
 564     return wxFileName::SetCwd( GetFullPath() ); 
 567 bool wxFileName::SetCwd( const wxString 
&cwd 
) 
 569     return ::wxSetWorkingDirectory( cwd 
); 
 572 void wxFileName::AssignHomeDir() 
 574     AssignDir(wxFileName::GetHomeDir()); 
 577 wxString 
wxFileName::GetHomeDir() 
 579     return ::wxGetHomeDir(); 
 584 void wxFileName::AssignTempFileName(const wxString
& prefix
, wxFile 
*fileTemp
) 
 586     wxString tempname 
= CreateTempFileName(prefix
, fileTemp
); 
 587     if ( tempname
.empty() ) 
 589         // error, failed to get temp file name 
 600 wxFileName::CreateTempFileName(const wxString
& prefix
, wxFile 
*fileTemp
) 
 602     wxString path
, dir
, name
; 
 604     // use the directory specified by the prefix 
 605     SplitPath(prefix
, &dir
, &name
, NULL 
/* extension */); 
 609         dir 
= wxGetenv(_T("TMPDIR")); 
 612             dir 
= wxGetenv(_T("TMP")); 
 615                 dir 
= wxGetenv(_T("TEMP")); 
 620 #if defined(__WXWINCE__) 
 623         // FIXME. Create \temp dir? 
 626     path 
= dir 
+ wxT("\\") + name
; 
 628     while (FileExists(path
)) 
 630         path 
= dir 
+ wxT("\\") + name 
; 
 635 #elif defined(__WINDOWS__) && !defined(__WXMICROWIN__) 
 639         if ( !::GetTempPath(MAX_PATH
, wxStringBuffer(dir
, MAX_PATH 
+ 1)) ) 
 641             wxLogLastError(_T("GetTempPath")); 
 646             // GetTempFileName() fails if we pass it an empty string 
 650     else // we have a dir to create the file in 
 652         // ensure we use only the back slashes as GetTempFileName(), unlike all 
 653         // the other APIs, is picky and doesn't accept the forward ones 
 654         dir
.Replace(_T("/"), _T("\\")); 
 657     if ( !::GetTempFileName(dir
, name
, 0, wxStringBuffer(path
, MAX_PATH 
+ 1)) ) 
 659         wxLogLastError(_T("GetTempFileName")); 
 668 #if defined(__DOS__) || defined(__OS2__) 
 670 #elif defined(__WXMAC__) 
 671         dir 
= wxMacFindFolder(short(kOnSystemDisk
), kTemporaryFolderType
, kCreateFolder
); 
 679     if ( !wxEndsWithPathSeparator(dir
) && 
 680             (name
.empty() || !wxIsPathSeparator(name
[0u])) ) 
 682         path 
+= wxFILE_SEP_PATH
; 
 687 #if defined(HAVE_MKSTEMP) 
 688     // scratch space for mkstemp() 
 689     path 
+= _T("XXXXXX"); 
 691     // we need to copy the path to the buffer in which mkstemp() can modify it 
 692     wxCharBuffer 
buf( wxConvFile
.cWX2MB( path 
) ); 
 694     // cast is safe because the string length doesn't change 
 695     int fdTemp 
= mkstemp( (char*)(const char*) buf 
); 
 698         // this might be not necessary as mkstemp() on most systems should have 
 699         // already done it but it doesn't hurt neither... 
 702     else // mkstemp() succeeded 
 704         path 
= wxConvFile
.cMB2WX( (const char*) buf 
); 
 706         // avoid leaking the fd 
 709             fileTemp
->Attach(fdTemp
); 
 716 #else // !HAVE_MKSTEMP 
 720     path 
+= _T("XXXXXX"); 
 722     wxCharBuffer buf 
= wxConvFile
.cWX2MB( path 
); 
 723     if ( !mktemp( (const char*) buf 
) ) 
 729         path 
= wxConvFile
.cMB2WX( (const char*) buf 
); 
 731 #else // !HAVE_MKTEMP (includes __DOS__) 
 732     // generate the unique file name ourselves 
 733     #if !defined(__DOS__) && !defined(__PALMOS__) && (!defined(__MWERKS__) || defined(__DARWIN__) ) 
 734     path 
<< (unsigned int)getpid(); 
 739     static const size_t numTries 
= 1000; 
 740     for ( size_t n 
= 0; n 
< numTries
; n
++ ) 
 742         // 3 hex digits is enough for numTries == 1000 < 4096 
 743         pathTry 
= path 
+ wxString::Format(_T("%.03x"), (unsigned int) n
); 
 744         if ( !FileExists(pathTry
) ) 
 753 #endif // HAVE_MKTEMP/!HAVE_MKTEMP 
 755 #endif // HAVE_MKSTEMP/!HAVE_MKSTEMP 
 757 #endif // Windows/!Windows 
 761         wxLogSysError(_("Failed to create a temporary file name")); 
 763     else if ( fileTemp 
&& !fileTemp
->IsOpened() ) 
 765         // open the file - of course, there is a race condition here, this is 
 766         // why we always prefer using mkstemp()... 
 768         // NB: GetTempFileName() under Windows creates the file, so using 
 769         //     write_excl there would fail 
 770         if ( !fileTemp
->Open(path
, 
 771 #if defined(__WINDOWS__) && !defined(__WXMICROWIN__) 
 776                              wxS_IRUSR 
| wxS_IWUSR
) ) 
 778             // FIXME: If !ok here should we loop and try again with another 
 779             //        file name?  That is the standard recourse if open(O_EXCL) 
 780             //        fails, though of course it should be protected against 
 781             //        possible infinite looping too. 
 783             wxLogError(_("Failed to open temporary file.")); 
 794 // ---------------------------------------------------------------------------- 
 795 // directory operations 
 796 // ---------------------------------------------------------------------------- 
 798 bool wxFileName::Mkdir( int perm
, int flags 
) 
 800     return wxFileName::Mkdir( GetFullPath(), perm
, flags 
); 
 803 bool wxFileName::Mkdir( const wxString
& dir
, int perm
, int flags 
) 
 805     if ( flags 
& wxPATH_MKDIR_FULL 
) 
 807         // split the path in components 
 809         filename
.AssignDir(dir
); 
 812         if ( filename
.HasVolume()) 
 814             currPath 
<< wxGetVolumeString(filename
.GetVolume(), wxPATH_NATIVE
); 
 817         wxArrayString dirs 
= filename
.GetDirs(); 
 818         size_t count 
= dirs
.GetCount(); 
 819         for ( size_t i 
= 0; i 
< count
; i
++ ) 
 822 #if defined(__WXMAC__) && !defined(__DARWIN__) 
 823             // relative pathnames are exactely the other way round under mac... 
 824                 !filename
.IsAbsolute() 
 826                 filename
.IsAbsolute() 
 829                 currPath 
+= wxFILE_SEP_PATH
; 
 832             if (!DirExists(currPath
)) 
 834                 if (!wxMkdir(currPath
, perm
)) 
 836                     // no need to try creating further directories 
 846     return ::wxMkdir( dir
, perm 
); 
 849 bool wxFileName::Rmdir() 
 851     return wxFileName::Rmdir( GetFullPath() ); 
 854 bool wxFileName::Rmdir( const wxString 
&dir 
) 
 856     return ::wxRmdir( dir 
); 
 859 // ---------------------------------------------------------------------------- 
 860 // path normalization 
 861 // ---------------------------------------------------------------------------- 
 863 bool wxFileName::Normalize(int flags
, 
 867     // deal with env vars renaming first as this may seriously change the path 
 868     if ( flags 
& wxPATH_NORM_ENV_VARS 
) 
 870         wxString pathOrig 
= GetFullPath(format
); 
 871         wxString path 
= wxExpandEnvVars(pathOrig
); 
 872         if ( path 
!= pathOrig 
) 
 879     // the existing path components 
 880     wxArrayString dirs 
= GetDirs(); 
 882     // the path to prepend in front to make the path absolute 
 885     format 
= GetFormat(format
); 
 887     // make the path absolute 
 888     if ( (flags 
& wxPATH_NORM_ABSOLUTE
) && !IsAbsolute(format
) ) 
 892             curDir
.AssignCwd(GetVolume()); 
 896             curDir
.AssignDir(cwd
); 
 899         // the path may be not absolute because it doesn't have the volume name 
 900         // but in this case we shouldn't modify the directory components of it 
 901         // but just set the current volume 
 902         if ( !HasVolume() && curDir
.HasVolume() ) 
 904             SetVolume(curDir
.GetVolume()); 
 908                 // yes, it was the case - we don't need curDir then 
 914     // handle ~ stuff under Unix only 
 915     if ( (format 
== wxPATH_UNIX
) && (flags 
& wxPATH_NORM_TILDE
) ) 
 917         if ( !dirs
.IsEmpty() ) 
 919             wxString dir 
= dirs
[0u]; 
 920             if ( !dir
.empty() && dir
[0u] == _T('~') ) 
 922                 curDir
.AssignDir(wxGetUserHome(dir
.c_str() + 1)); 
 929     // transform relative path into abs one 
 932         wxArrayString dirsNew 
= curDir
.GetDirs(); 
 933         size_t count 
= dirs
.GetCount(); 
 934         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 936             dirsNew
.Add(dirs
[n
]); 
 942     // now deal with ".", ".." and the rest 
 944     size_t count 
= dirs
.GetCount(); 
 945     for ( size_t n 
= 0; n 
< count
; n
++ ) 
 947         wxString dir 
= dirs
[n
]; 
 949         if ( flags 
& wxPATH_NORM_DOTS 
) 
 951             if ( dir 
== wxT(".") ) 
 957             if ( dir 
== wxT("..") ) 
 959                 if ( m_dirs
.IsEmpty() ) 
 961                     wxLogError(_("The path '%s' contains too many \"..\"!"), 
 962                                GetFullPath().c_str()); 
 966                 m_dirs
.RemoveAt(m_dirs
.GetCount() - 1); 
 971         if ( (flags 
& wxPATH_NORM_CASE
) && !IsCaseSensitive(format
) ) 
 979 #if defined(__WIN32__) && !defined(__WXWINCE__) && wxUSE_OLE 
 980     if ( (flags 
& wxPATH_NORM_SHORTCUT
) ) 
 983         if (GetShortcutTarget(GetFullPath(format
), filename
)) 
 985             // Repeat this since we may now have a new path 
 986             if ( (flags 
& wxPATH_NORM_CASE
) && !IsCaseSensitive(format
) ) 
 988                 filename
.MakeLower(); 
 996     if ( (flags 
& wxPATH_NORM_CASE
) && !IsCaseSensitive(format
) ) 
 998         // VZ: expand env vars here too? 
1000         m_volume
.MakeLower(); 
1005     // we do have the path now 
1007     // NB: need to do this before (maybe) calling Assign() below 
1010 #if defined(__WIN32__) 
1011     if ( (flags 
& wxPATH_NORM_LONG
) && (format 
== wxPATH_DOS
) ) 
1013         Assign(GetLongPath()); 
1020 // ---------------------------------------------------------------------------- 
1021 // get the shortcut target 
1022 // ---------------------------------------------------------------------------- 
1024 // WinCE (3) doesn't have CLSID_ShellLink, IID_IShellLink definitions. 
1025 // The .lnk file is a plain text file so it should be easy to 
1026 // make it work. Hint from Google Groups: 
1027 // "If you open up a lnk file, you'll see a 
1028 // number, followed by a pound sign (#), followed by more text. The 
1029 // number is the number of characters that follows the pound sign. The 
1030 // characters after the pound sign are the command line (which _can_ 
1031 // include arguments) to be executed. Any path (e.g. \windows\program 
1032 // files\myapp.exe) that includes spaces needs to be enclosed in 
1033 // quotation marks." 
1035 #if defined(__WIN32__) && !defined(__WXWINCE__) && wxUSE_OLE 
1036 // The following lines are necessary under WinCE 
1037 // #include "wx/msw/private.h" 
1038 // #include <ole2.h> 
1040 #if defined(__WXWINCE__) 
1041 #include <shlguid.h> 
1044 bool wxFileName::GetShortcutTarget(const wxString
& shortcutPath
, wxString
& targetFilename
, wxString
* arguments
) 
1046     wxString path
, file
, ext
; 
1047     wxSplitPath(shortcutPath
, & path
, & file
, & ext
); 
1051     bool success 
= false; 
1053     // Assume it's not a shortcut if it doesn't end with lnk 
1054     if (ext
.CmpNoCase(wxT("lnk"))!=0) 
1057     // create a ShellLink object 
1058     hres 
= CoCreateInstance(CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
, 
1059                             IID_IShellLink
, (LPVOID
*) &psl
); 
1061     if (SUCCEEDED(hres
)) 
1064         hres 
= psl
->QueryInterface( IID_IPersistFile
, (LPVOID 
*) &ppf
); 
1065         if (SUCCEEDED(hres
)) 
1067             WCHAR wsz
[MAX_PATH
]; 
1069             MultiByteToWideChar(CP_ACP
, MB_PRECOMPOSED
, shortcutPath
.mb_str(), -1, wsz
, 
1072             hres 
= ppf
->Load(wsz
, 0); 
1073             if (SUCCEEDED(hres
)) 
1076                 // Wrong prototype in early versions 
1077 #if defined(__MINGW32__) && !wxCHECK_W32API_VERSION(2, 2) 
1078                 psl
->GetPath((CHAR
*) buf
, 2048, NULL
, SLGP_UNCPRIORITY
); 
1080                 psl
->GetPath(buf
, 2048, NULL
, SLGP_UNCPRIORITY
); 
1082                 targetFilename 
= wxString(buf
); 
1083                 success 
= (shortcutPath 
!= targetFilename
); 
1085                 psl
->GetArguments(buf
, 2048); 
1087                 if (!args
.empty() && arguments
) 
1100 // ---------------------------------------------------------------------------- 
1101 // absolute/relative paths 
1102 // ---------------------------------------------------------------------------- 
1104 bool wxFileName::IsAbsolute(wxPathFormat format
) const 
1106     // if our path doesn't start with a path separator, it's not an absolute 
1111     if ( !GetVolumeSeparator(format
).empty() ) 
1113         // this format has volumes and an absolute path must have one, it's not 
1114         // enough to have the full path to bean absolute file under Windows 
1115         if ( GetVolume().empty() ) 
1122 bool wxFileName::MakeRelativeTo(const wxString
& pathBase
, wxPathFormat format
) 
1124     wxFileName fnBase 
= wxFileName::DirName(pathBase
, format
); 
1126     // get cwd only once - small time saving 
1127     wxString cwd 
= wxGetCwd(); 
1128     Normalize(wxPATH_NORM_ALL 
& ~wxPATH_NORM_CASE
, cwd
, format
); 
1129     fnBase
.Normalize(wxPATH_NORM_ALL 
& ~wxPATH_NORM_CASE
, cwd
, format
); 
1131     bool withCase 
= IsCaseSensitive(format
); 
1133     // we can't do anything if the files live on different volumes 
1134     if ( !GetVolume().IsSameAs(fnBase
.GetVolume(), withCase
) ) 
1140     // same drive, so we don't need our volume 
1143     // remove common directories starting at the top 
1144     while ( !m_dirs
.IsEmpty() && !fnBase
.m_dirs
.IsEmpty() && 
1145                 m_dirs
[0u].IsSameAs(fnBase
.m_dirs
[0u], withCase
) ) 
1148         fnBase
.m_dirs
.RemoveAt(0); 
1151     // add as many ".." as needed 
1152     size_t count 
= fnBase
.m_dirs
.GetCount(); 
1153     for ( size_t i 
= 0; i 
< count
; i
++ ) 
1155         m_dirs
.Insert(wxT(".."), 0u); 
1158     if ( format 
== wxPATH_UNIX 
|| format 
== wxPATH_DOS 
) 
1160         // a directory made relative with respect to itself is '.' under Unix 
1161         // and DOS, by definition (but we don't have to insert "./" for the 
1163         if ( m_dirs
.IsEmpty() && IsDir() ) 
1165             m_dirs
.Add(_T('.')); 
1175 // ---------------------------------------------------------------------------- 
1176 // filename kind tests 
1177 // ---------------------------------------------------------------------------- 
1179 bool wxFileName::SameAs(const wxFileName
& filepath
, wxPathFormat format
) const 
1181     wxFileName fn1 
= *this, 
1184     // get cwd only once - small time saving 
1185     wxString cwd 
= wxGetCwd(); 
1186     fn1
.Normalize(wxPATH_NORM_ALL 
| wxPATH_NORM_CASE
, cwd
, format
); 
1187     fn2
.Normalize(wxPATH_NORM_ALL 
| wxPATH_NORM_CASE
, cwd
, format
); 
1189     if ( fn1
.GetFullPath() == fn2
.GetFullPath() ) 
1192     // TODO: compare inodes for Unix, this works even when filenames are 
1193     //       different but files are the same (symlinks) (VZ) 
1199 bool wxFileName::IsCaseSensitive( wxPathFormat format 
) 
1201     // only Unix filenames are truely case-sensitive 
1202     return GetFormat(format
) == wxPATH_UNIX
; 
1206 wxString 
wxFileName::GetForbiddenChars(wxPathFormat format
) 
1208     // Inits to forbidden characters that are common to (almost) all platforms. 
1209     wxString strForbiddenChars 
= wxT("*?"); 
1211     // If asserts, wxPathFormat has been changed. In case of a new path format 
1212     // addition, the following code might have to be updated. 
1213     wxCOMPILE_TIME_ASSERT(wxPATH_MAX 
== 5, wxPathFormatChanged
); 
1214     switch ( GetFormat(format
) ) 
1217             wxFAIL_MSG( wxT("Unknown path format") ); 
1218             // !! Fall through !! 
1224             // On a Mac even names with * and ? are allowed (Tested with OS 
1225             // 9.2.1 and OS X 10.2.5) 
1226             strForbiddenChars 
= wxEmptyString
; 
1230             strForbiddenChars 
+= wxT("\\/:\"<>|"); 
1237     return strForbiddenChars
; 
1241 wxString 
wxFileName::GetVolumeSeparator(wxPathFormat 
WXUNUSED_IN_WINCE(format
)) 
1244     return wxEmptyString
; 
1248     if ( (GetFormat(format
) == wxPATH_DOS
) || 
1249          (GetFormat(format
) == wxPATH_VMS
) ) 
1251         sepVol 
= wxFILE_SEP_DSK
; 
1260 wxString 
wxFileName::GetPathSeparators(wxPathFormat format
) 
1263     switch ( GetFormat(format
) ) 
1266             // accept both as native APIs do but put the native one first as 
1267             // this is the one we use in GetFullPath() 
1268             seps 
<< wxFILE_SEP_PATH_DOS 
<< wxFILE_SEP_PATH_UNIX
; 
1272             wxFAIL_MSG( _T("Unknown wxPATH_XXX style") ); 
1276             seps 
= wxFILE_SEP_PATH_UNIX
; 
1280             seps 
= wxFILE_SEP_PATH_MAC
; 
1284             seps 
= wxFILE_SEP_PATH_VMS
; 
1292 wxString 
wxFileName::GetPathTerminators(wxPathFormat format
) 
1294     format 
= GetFormat(format
); 
1296     // under VMS the end of the path is ']', not the path separator used to 
1297     // separate the components 
1298     return format 
== wxPATH_VMS 
? wxString(_T(']')) : GetPathSeparators(format
); 
1302 bool wxFileName::IsPathSeparator(wxChar ch
, wxPathFormat format
) 
1304     // wxString::Find() doesn't work as expected with NUL - it will always find 
1305     // it, so test for it separately 
1306     return ch 
!= _T('\0') && GetPathSeparators(format
).Find(ch
) != wxNOT_FOUND
; 
1309 // ---------------------------------------------------------------------------- 
1310 // path components manipulation 
1311 // ---------------------------------------------------------------------------- 
1313 /* static */ bool wxFileName::IsValidDirComponent(const wxString
& dir
) 
1317         wxFAIL_MSG( _T("empty directory passed to wxFileName::InsertDir()") ); 
1322     const size_t len 
= dir
.length(); 
1323     for ( size_t n 
= 0; n 
< len
; n
++ ) 
1325         if ( dir
[n
] == GetVolumeSeparator() || IsPathSeparator(dir
[n
]) ) 
1327             wxFAIL_MSG( _T("invalid directory component in wxFileName") ); 
1336 void wxFileName::AppendDir( const wxString
& dir 
) 
1338     if ( IsValidDirComponent(dir
) ) 
1342 void wxFileName::PrependDir( const wxString
& dir 
) 
1347 void wxFileName::InsertDir(size_t before
, const wxString
& dir
) 
1349     if ( IsValidDirComponent(dir
) ) 
1350         m_dirs
.Insert(dir
, before
); 
1353 void wxFileName::RemoveDir(size_t pos
) 
1355     m_dirs
.RemoveAt(pos
); 
1358 // ---------------------------------------------------------------------------- 
1360 // ---------------------------------------------------------------------------- 
1362 void wxFileName::SetFullName(const wxString
& fullname
) 
1364     SplitPath(fullname
, NULL 
/* no volume */, NULL 
/* no path */, 
1365                         &m_name
, &m_ext
, &m_hasExt
); 
1368 wxString 
wxFileName::GetFullName() const 
1370     wxString fullname 
= m_name
; 
1373         fullname 
<< wxFILE_SEP_EXT 
<< m_ext
; 
1379 wxString 
wxFileName::GetPath( int flags
, wxPathFormat format 
) const 
1381     format 
= GetFormat( format 
); 
1385     // return the volume with the path as well if requested 
1386     if ( flags 
& wxPATH_GET_VOLUME 
) 
1388         fullpath 
+= wxGetVolumeString(GetVolume(), format
); 
1391     // the leading character 
1396                 fullpath 
+= wxFILE_SEP_PATH_MAC
; 
1401                 fullpath 
+= wxFILE_SEP_PATH_DOS
; 
1405             wxFAIL_MSG( wxT("Unknown path format") ); 
1411                 // normally the absolute file names start with a slash 
1412                 // with one exception: the ones like "~/foo.bar" don't 
1414                 if ( m_dirs
.IsEmpty() || m_dirs
[0u] != _T('~') ) 
1416                     fullpath 
+= wxFILE_SEP_PATH_UNIX
; 
1422             // no leading character here but use this place to unset 
1423             // wxPATH_GET_SEPARATOR flag: under VMS it doesn't make sense 
1424             // as, if I understand correctly, there should never be a dot 
1425             // before the closing bracket 
1426             flags 
&= ~wxPATH_GET_SEPARATOR
; 
1429     if ( m_dirs
.empty() ) 
1431         // there is nothing more 
1435     // then concatenate all the path components using the path separator 
1436     if ( format 
== wxPATH_VMS 
) 
1438         fullpath 
+= wxT('['); 
1441     const size_t dirCount 
= m_dirs
.GetCount(); 
1442     for ( size_t i 
= 0; i 
< dirCount
; i
++ ) 
1447                 if ( m_dirs
[i
] == wxT(".") ) 
1449                     // skip appending ':', this shouldn't be done in this 
1450                     // case as "::" is interpreted as ".." under Unix 
1454                 // convert back from ".." to nothing 
1455                 if ( !m_dirs
[i
].IsSameAs(wxT("..")) ) 
1456                      fullpath 
+= m_dirs
[i
]; 
1460                 wxFAIL_MSG( wxT("Unexpected path format") ); 
1461                 // still fall through 
1465                 fullpath 
+= m_dirs
[i
]; 
1469                 // TODO: What to do with ".." under VMS 
1471                 // convert back from ".." to nothing 
1472                 if ( !m_dirs
[i
].IsSameAs(wxT("..")) ) 
1473                     fullpath 
+= m_dirs
[i
]; 
1477         if ( (flags 
& wxPATH_GET_SEPARATOR
) || (i 
!= dirCount 
- 1) ) 
1478             fullpath 
+= GetPathSeparator(format
); 
1481     if ( format 
== wxPATH_VMS 
) 
1483         fullpath 
+= wxT(']'); 
1489 wxString 
wxFileName::GetFullPath( wxPathFormat format 
) const 
1491     // we already have a function to get the path 
1492     wxString fullpath 
= GetPath(wxPATH_GET_VOLUME 
| wxPATH_GET_SEPARATOR
, 
1495     // now just add the file name and extension to it 
1496     fullpath 
+= GetFullName(); 
1501 // Return the short form of the path (returns identity on non-Windows platforms) 
1502 wxString 
wxFileName::GetShortPath() const 
1504     wxString 
path(GetFullPath()); 
1506 #if defined(__WXMSW__) && defined(__WIN32__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__) 
1507     DWORD sz 
= ::GetShortPathName(path
, NULL
, 0); 
1511         if ( ::GetShortPathName
 
1514                 wxStringBuffer(pathOut
, sz
), 
1526 // Return the long form of the path (returns identity on non-Windows platforms) 
1527 wxString 
wxFileName::GetLongPath() const 
1530              path 
= GetFullPath(); 
1532 #if defined(__WIN32__) && !defined(__WXMICROWIN__) 
1534 #if wxUSE_DYNAMIC_LOADER 
1535     typedef DWORD (WINAPI 
*GET_LONG_PATH_NAME
)(const wxChar 
*, wxChar 
*, DWORD
); 
1537     // this is MT-safe as in the worst case we're going to resolve the function 
1538     // twice -- but as the result is the same in both threads, it's ok 
1539     static GET_LONG_PATH_NAME s_pfnGetLongPathName 
= NULL
; 
1540     if ( !s_pfnGetLongPathName 
) 
1542         static bool s_triedToLoad 
= false; 
1544         if ( !s_triedToLoad 
) 
1546             s_triedToLoad 
= true; 
1548             wxDynamicLibrary 
dllKernel(_T("kernel32")); 
1550             const wxChar
* GetLongPathName 
= _T("GetLongPathName") 
1555 #endif // Unicode/ANSI 
1557             if ( dllKernel
.HasSymbol(GetLongPathName
) ) 
1559                 s_pfnGetLongPathName 
= (GET_LONG_PATH_NAME
) 
1560                     dllKernel
.GetSymbol(GetLongPathName
); 
1563             // note that kernel32.dll can be unloaded, it stays in memory 
1564             // anyhow as all Win32 programs link to it and so it's safe to call 
1565             // GetLongPathName() even after unloading it 
1569     if ( s_pfnGetLongPathName 
) 
1571         DWORD dwSize 
= (*s_pfnGetLongPathName
)(path
, NULL
, 0); 
1574             if ( (*s_pfnGetLongPathName
) 
1577                   wxStringBuffer(pathOut
, dwSize
), 
1585 #endif // wxUSE_DYNAMIC_LOADER 
1587     // The OS didn't support GetLongPathName, or some other error. 
1588     // We need to call FindFirstFile on each component in turn. 
1590     WIN32_FIND_DATA findFileData
; 
1594         pathOut 
= GetVolume() + 
1595                   GetVolumeSeparator(wxPATH_DOS
) + 
1596                   GetPathSeparator(wxPATH_DOS
); 
1598         pathOut 
= wxEmptyString
; 
1600     wxArrayString dirs 
= GetDirs(); 
1601     dirs
.Add(GetFullName()); 
1605     size_t count 
= dirs
.GetCount(); 
1606     for ( size_t i 
= 0; i 
< count
; i
++ ) 
1608         // We're using pathOut to collect the long-name path, but using a 
1609         // temporary for appending the last path component which may be 
1611         tmpPath 
= pathOut 
+ dirs
[i
]; 
1613         if ( tmpPath
.empty() ) 
1616         // can't see this being necessary? MF 
1617         if ( tmpPath
.Last() == GetVolumeSeparator(wxPATH_DOS
) ) 
1619             // Can't pass a drive and root dir to FindFirstFile, 
1620             // so continue to next dir 
1621             tmpPath 
+= wxFILE_SEP_PATH
; 
1626         hFind 
= ::FindFirstFile(tmpPath
, &findFileData
); 
1627         if (hFind 
== INVALID_HANDLE_VALUE
) 
1629             // Error: most likely reason is that path doesn't exist, so 
1630             // append any unprocessed parts and return 
1631             for ( i 
+= 1; i 
< count
; i
++ ) 
1632                 tmpPath 
+= wxFILE_SEP_PATH 
+ dirs
[i
]; 
1637         pathOut 
+= findFileData
.cFileName
; 
1638         if ( (i 
< (count
-1)) ) 
1639             pathOut 
+= wxFILE_SEP_PATH
; 
1645 #endif // Win32/!Win32 
1650 wxPathFormat 
wxFileName::GetFormat( wxPathFormat format 
) 
1652     if (format 
== wxPATH_NATIVE
) 
1654 #if defined(__WXMSW__) || defined(__OS2__) || defined(__DOS__) 
1655         format 
= wxPATH_DOS
; 
1656 #elif defined(__WXMAC__) && !defined(__DARWIN__) 
1657         format 
= wxPATH_MAC
; 
1658 #elif defined(__VMS) 
1659         format 
= wxPATH_VMS
; 
1661         format 
= wxPATH_UNIX
; 
1667 // ---------------------------------------------------------------------------- 
1668 // path splitting function 
1669 // ---------------------------------------------------------------------------- 
1673 wxFileName::SplitVolume(const wxString
& fullpathWithVolume
, 
1674                         wxString 
*pstrVolume
, 
1676                         wxPathFormat format
) 
1678     format 
= GetFormat(format
); 
1680     wxString fullpath 
= fullpathWithVolume
; 
1682     // special Windows UNC paths hack: transform \\share\path into share:path 
1683     if ( format 
== wxPATH_DOS 
) 
1685         if ( fullpath
.length() >= 4 && 
1686                 fullpath
[0u] == wxFILE_SEP_PATH_DOS 
&& 
1687                     fullpath
[1u] == wxFILE_SEP_PATH_DOS 
) 
1689             fullpath
.erase(0, 2); 
1691             size_t posFirstSlash 
= 
1692                 fullpath
.find_first_of(GetPathTerminators(format
)); 
1693             if ( posFirstSlash 
!= wxString::npos 
) 
1695                 fullpath
[posFirstSlash
] = wxFILE_SEP_DSK
; 
1697                 // UNC paths are always absolute, right? (FIXME) 
1698                 fullpath
.insert(posFirstSlash 
+ 1, 1, wxFILE_SEP_PATH_DOS
); 
1703     // We separate the volume here 
1704     if ( format 
== wxPATH_DOS 
|| format 
== wxPATH_VMS 
) 
1706         wxString sepVol 
= GetVolumeSeparator(format
); 
1708         size_t posFirstColon 
= fullpath
.find_first_of(sepVol
); 
1709         if ( posFirstColon 
!= wxString::npos 
) 
1713                 *pstrVolume 
= fullpath
.Left(posFirstColon
); 
1716             // remove the volume name and the separator from the full path 
1717             fullpath
.erase(0, posFirstColon 
+ sepVol
.length()); 
1722         *pstrPath 
= fullpath
; 
1726 void wxFileName::SplitPath(const wxString
& fullpathWithVolume
, 
1727                            wxString 
*pstrVolume
, 
1732                            wxPathFormat format
) 
1734     format 
= GetFormat(format
); 
1737     SplitVolume(fullpathWithVolume
, pstrVolume
, &fullpath
, format
); 
1739     // find the positions of the last dot and last path separator in the path 
1740     size_t posLastDot 
= fullpath
.find_last_of(wxFILE_SEP_EXT
); 
1741     size_t posLastSlash 
= fullpath
.find_last_of(GetPathTerminators(format
)); 
1743     // check whether this dot occurs at the very beginning of a path component 
1744     if ( (posLastDot 
!= wxString::npos
) && 
1746             IsPathSeparator(fullpath
[posLastDot 
- 1]) || 
1747             (format 
== wxPATH_VMS 
&& fullpath
[posLastDot 
- 1] == _T(']'))) ) 
1749         // dot may be (and commonly -- at least under Unix -- is) the first 
1750         // character of the filename, don't treat the entire filename as 
1751         // extension in this case 
1752         posLastDot 
= wxString::npos
; 
1755     // if we do have a dot and a slash, check that the dot is in the name part 
1756     if ( (posLastDot 
!= wxString::npos
) && 
1757          (posLastSlash 
!= wxString::npos
) && 
1758          (posLastDot 
< posLastSlash
) ) 
1760         // the dot is part of the path, not the start of the extension 
1761         posLastDot 
= wxString::npos
; 
1764     // now fill in the variables provided by user 
1767         if ( posLastSlash 
== wxString::npos 
) 
1774             // take everything up to the path separator but take care to make 
1775             // the path equal to something like '/', not empty, for the files 
1776             // immediately under root directory 
1777             size_t len 
= posLastSlash
; 
1779             // this rule does not apply to mac since we do not start with colons (sep) 
1780             // except for relative paths 
1781             if ( !len 
&& format 
!= wxPATH_MAC
) 
1784             *pstrPath 
= fullpath
.Left(len
); 
1786             // special VMS hack: remove the initial bracket 
1787             if ( format 
== wxPATH_VMS 
) 
1789                 if ( (*pstrPath
)[0u] == _T('[') ) 
1790                     pstrPath
->erase(0, 1); 
1797         // take all characters starting from the one after the last slash and 
1798         // up to, but excluding, the last dot 
1799         size_t nStart 
= posLastSlash 
== wxString::npos 
? 0 : posLastSlash 
+ 1; 
1801         if ( posLastDot 
== wxString::npos 
) 
1803             // take all until the end 
1804             count 
= wxString::npos
; 
1806         else if ( posLastSlash 
== wxString::npos 
) 
1810         else // have both dot and slash 
1812             count 
= posLastDot 
- posLastSlash 
- 1; 
1815         *pstrName 
= fullpath
.Mid(nStart
, count
); 
1818     // finally deal with the extension here: we have an added complication that 
1819     // extension may be empty (but present) as in "foo." where trailing dot 
1820     // indicates the empty extension at the end -- and hence we must remember 
1821     // that we have it independently of pstrExt 
1822     if ( posLastDot 
== wxString::npos 
) 
1832         // take everything after the dot 
1834             *pstrExt 
= fullpath
.Mid(posLastDot 
+ 1); 
1841 void wxFileName::SplitPath(const wxString
& fullpath
, 
1845                            wxPathFormat format
) 
1848     SplitPath(fullpath
, &volume
, path
, name
, ext
, format
); 
1852         path
->Prepend(wxGetVolumeString(volume
, format
)); 
1856 // ---------------------------------------------------------------------------- 
1858 // ---------------------------------------------------------------------------- 
1862 bool wxFileName::SetTimes(const wxDateTime 
*dtAccess
, 
1863                           const wxDateTime 
*dtMod
, 
1864                           const wxDateTime 
*dtCreate
) 
1866 #if defined(__WIN32__) 
1869         // VZ: please let me know how to do this if you can 
1870         wxFAIL_MSG( _T("SetTimes() not implemented for the directories") ); 
1874         wxFileHandle 
fh(GetFullPath(), wxFileHandle::Write
); 
1877             FILETIME ftAccess
, ftCreate
, ftWrite
; 
1880                 ConvertWxToFileTime(&ftCreate
, *dtCreate
); 
1882                 ConvertWxToFileTime(&ftAccess
, *dtAccess
); 
1884                 ConvertWxToFileTime(&ftWrite
, *dtMod
); 
1886             if ( ::SetFileTime(fh
, 
1887                                dtCreate 
? &ftCreate 
: NULL
, 
1888                                dtAccess 
? &ftAccess 
: NULL
, 
1889                                dtMod 
? &ftWrite 
: NULL
) ) 
1895 #elif defined(__UNIX_LIKE__) || (defined(__DOS__) && defined(__WATCOMC__)) 
1896     wxUnusedVar(dtCreate
); 
1898     if ( !dtAccess 
&& !dtMod 
) 
1900         // can't modify the creation time anyhow, don't try 
1904     // if dtAccess or dtMod is not specified, use the other one (which must be 
1905     // non NULL because of the test above) for both times 
1907     utm
.actime 
= dtAccess 
? dtAccess
->GetTicks() : dtMod
->GetTicks(); 
1908     utm
.modtime 
= dtMod 
? dtMod
->GetTicks() : dtAccess
->GetTicks(); 
1909     if ( utime(GetFullPath().fn_str(), &utm
) == 0 ) 
1913 #else // other platform 
1914     wxUnusedVar(dtAccess
); 
1916     wxUnusedVar(dtCreate
); 
1919     wxLogSysError(_("Failed to modify file times for '%s'"), 
1920                   GetFullPath().c_str()); 
1925 bool wxFileName::Touch() 
1927 #if defined(__UNIX_LIKE__) 
1928     // under Unix touching file is simple: just pass NULL to utime() 
1929     if ( utime(GetFullPath().fn_str(), NULL
) == 0 ) 
1934     wxLogSysError(_("Failed to touch the file '%s'"), GetFullPath().c_str()); 
1937 #else // other platform 
1938     wxDateTime dtNow 
= wxDateTime::Now(); 
1940     return SetTimes(&dtNow
, &dtNow
, NULL 
/* don't change create time */); 
1944 bool wxFileName::GetTimes(wxDateTime 
*dtAccess
, 
1946                           wxDateTime 
*dtCreate
) const 
1948 #if defined(__WIN32__) 
1949     // we must use different methods for the files and directories under 
1950     // Windows as CreateFile(GENERIC_READ) doesn't work for the directories and 
1951     // CreateFile(FILE_FLAG_BACKUP_SEMANTICS) works -- but only under NT and 
1954     FILETIME ftAccess
, ftCreate
, ftWrite
; 
1957         // implemented in msw/dir.cpp 
1958         extern bool wxGetDirectoryTimes(const wxString
& dirname
, 
1959                                         FILETIME 
*, FILETIME 
*, FILETIME 
*); 
1961         // we should pass the path without the trailing separator to 
1962         // wxGetDirectoryTimes() 
1963         ok 
= wxGetDirectoryTimes(GetPath(wxPATH_GET_VOLUME
), 
1964                                  &ftAccess
, &ftCreate
, &ftWrite
); 
1968         wxFileHandle 
fh(GetFullPath(), wxFileHandle::Read
); 
1971             ok 
= ::GetFileTime(fh
, 
1972                                dtCreate 
? &ftCreate 
: NULL
, 
1973                                dtAccess 
? &ftAccess 
: NULL
, 
1974                                dtMod 
? &ftWrite 
: NULL
) != 0; 
1985             ConvertFileTimeToWx(dtCreate
, ftCreate
); 
1987             ConvertFileTimeToWx(dtAccess
, ftAccess
); 
1989             ConvertFileTimeToWx(dtMod
, ftWrite
); 
1993 #elif defined(__UNIX_LIKE__) || defined(__WXMAC__) || defined(__OS2__) || (defined(__DOS__) && defined(__WATCOMC__)) 
1995     if ( wxStat( GetFullPath().c_str(), &stBuf
) == 0 ) 
1998             dtAccess
->Set(stBuf
.st_atime
); 
2000             dtMod
->Set(stBuf
.st_mtime
); 
2002             dtCreate
->Set(stBuf
.st_ctime
); 
2006 #else // other platform 
2007     wxUnusedVar(dtAccess
); 
2009     wxUnusedVar(dtCreate
); 
2012     wxLogSysError(_("Failed to retrieve file times for '%s'"), 
2013                   GetFullPath().c_str()); 
2018 #endif // wxUSE_DATETIME 
2022 const short kMacExtensionMaxLength 
= 16 ; 
2023 class MacDefaultExtensionRecord
 
2026   MacDefaultExtensionRecord() 
2029     m_type 
= m_creator 
= 0 ; 
2031   MacDefaultExtensionRecord( const MacDefaultExtensionRecord
& from 
) 
2033     wxStrcpy( m_ext 
, from
.m_ext 
) ; 
2034     m_type 
= from
.m_type 
; 
2035     m_creator 
= from
.m_creator 
; 
2037   MacDefaultExtensionRecord( const wxChar 
* extension 
, OSType type 
, OSType creator 
) 
2039     wxStrncpy( m_ext 
, extension 
, kMacExtensionMaxLength 
) ; 
2040     m_ext
[kMacExtensionMaxLength
] = 0 ; 
2042     m_creator 
= creator 
; 
2044   wxChar m_ext
[kMacExtensionMaxLength
] ; 
2049 #include "wx/dynarray.h" 
2050 WX_DECLARE_OBJARRAY(MacDefaultExtensionRecord
, MacDefaultExtensionArray
) ; 
2052 bool gMacDefaultExtensionsInited 
= false ; 
2054 #include "wx/arrimpl.cpp" 
2056 WX_DEFINE_EXPORTED_OBJARRAY(MacDefaultExtensionArray
) ; 
2058 MacDefaultExtensionArray gMacDefaultExtensions 
; 
2060 // load the default extensions 
2061 MacDefaultExtensionRecord gDefaults
[] = 
2063     MacDefaultExtensionRecord( wxT("txt") , 'TEXT' , 'ttxt' ) , 
2064     MacDefaultExtensionRecord( wxT("tif") , 'TIFF' , '****' ) , 
2065     MacDefaultExtensionRecord( wxT("jpg") , 'JPEG' , '****' ) , 
2068 static void MacEnsureDefaultExtensionsLoaded() 
2070     if ( !gMacDefaultExtensionsInited 
) 
2072         // we could load the pc exchange prefs here too 
2073         for ( size_t i 
= 0 ; i 
< WXSIZEOF( gDefaults 
) ; ++i 
) 
2075             gMacDefaultExtensions
.Add( gDefaults
[i
] ) ; 
2077         gMacDefaultExtensionsInited 
= true ; 
2081 bool wxFileName::MacSetTypeAndCreator( wxUint32 type 
, wxUint32 creator 
) 
2084     FSCatalogInfo catInfo
; 
2087     if ( wxMacPathToFSRef( GetFullPath() , &fsRef 
) == noErr 
) 
2089         if ( FSGetCatalogInfo (&fsRef
, kFSCatInfoFinderInfo
, &catInfo
, NULL
, NULL
, NULL
) == noErr 
) 
2091             finfo 
= (FileInfo
*)&catInfo
.finderInfo
; 
2092             finfo
->fileType 
= type 
; 
2093             finfo
->fileCreator 
= creator 
; 
2094             FSSetCatalogInfo( &fsRef
, kFSCatInfoFinderInfo
, &catInfo 
) ; 
2101 bool wxFileName::MacGetTypeAndCreator( wxUint32 
*type 
, wxUint32 
*creator 
) 
2104     FSCatalogInfo catInfo
; 
2107     if ( wxMacPathToFSRef( GetFullPath() , &fsRef 
) == noErr 
) 
2109         if ( FSGetCatalogInfo (&fsRef
, kFSCatInfoFinderInfo
, &catInfo
, NULL
, NULL
, NULL
) == noErr 
) 
2111             finfo 
= (FileInfo
*)&catInfo
.finderInfo
; 
2112             *type 
= finfo
->fileType 
; 
2113             *creator 
= finfo
->fileCreator 
; 
2120 bool wxFileName::MacSetDefaultTypeAndCreator() 
2122     wxUint32 type 
, creator 
; 
2123     if ( wxFileName::MacFindDefaultTypeAndCreator(GetExt() , &type 
, 
2126         return MacSetTypeAndCreator( type 
, creator 
) ; 
2131 bool wxFileName::MacFindDefaultTypeAndCreator( const wxString
& ext 
, wxUint32 
*type 
, wxUint32 
*creator 
) 
2133   MacEnsureDefaultExtensionsLoaded() ; 
2134   wxString extl 
= ext
.Lower() ; 
2135   for( int i 
= gMacDefaultExtensions
.Count() - 1 ; i 
>= 0 ; --i 
) 
2137     if ( gMacDefaultExtensions
.Item(i
).m_ext 
== extl 
) 
2139       *type 
= gMacDefaultExtensions
.Item(i
).m_type 
; 
2140       *creator 
= gMacDefaultExtensions
.Item(i
).m_creator 
; 
2147 void wxFileName::MacRegisterDefaultTypeAndCreator( const wxString
& ext 
, wxUint32 type 
, wxUint32 creator 
) 
2149   MacEnsureDefaultExtensionsLoaded() ; 
2150   MacDefaultExtensionRecord rec 
; 
2152   rec
.m_creator 
= creator 
; 
2153   wxStrncpy( rec
.m_ext 
, ext
.Lower().c_str() , kMacExtensionMaxLength 
) ; 
2154   gMacDefaultExtensions
.Add( rec 
) ;