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 license 
  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 specyfication 
  51                  . separator between directory and subdirectory 
  52                  ] between directory and file 
  55 // ============================================================================ 
  57 // ============================================================================ 
  59 // ---------------------------------------------------------------------------- 
  61 // ---------------------------------------------------------------------------- 
  64 #pragma implementation "filename.h" 
  67 // For compilers that support precompilation, includes "wx.h". 
  68 #include "wx/wxprec.h" 
  80 #include "wx/filename.h" 
  81 #include "wx/tokenzr.h" 
  82 #include "wx/config.h"          // for wxExpandEnvVars 
  85 #include "wx/dynlib.h" 
  87 // For GetShort/LongPathName 
  90 #include "wx/msw/winundef.h" 
  93 #if defined(__WXMAC__) 
  94   #include  "wx/mac/private.h"  // includes mac headers 
  97 // utime() is POSIX so should normally be available on all Unices 
  99 #include <sys/types.h> 
 101 #include <sys/stat.h> 
 117 #include <sys/utime.h> 
 118 #include <sys/stat.h> 
 127 // ---------------------------------------------------------------------------- 
 129 // ---------------------------------------------------------------------------- 
 131 // small helper class which opens and closes the file - we use it just to get 
 132 // a file handle for the given file name to pass it to some Win32 API function 
 133 #if defined(__WIN32__) && !defined(__WXMICROWIN__) 
 144     wxFileHandle(const wxString
& filename
, OpenMode mode
) 
 146         m_hFile 
= ::CreateFile
 
 149                      mode 
== Read 
? GENERIC_READ    
// access mask 
 152                      NULL
,                          // no secutity attr 
 153                      OPEN_EXISTING
,                 // creation disposition 
 155                      NULL                           
// no template file 
 158         if ( m_hFile 
== INVALID_HANDLE_VALUE 
) 
 160             wxLogSysError(_("Failed to open '%s' for %s"), 
 162                           mode 
== Read 
? _("reading") : _("writing")); 
 168         if ( m_hFile 
!= INVALID_HANDLE_VALUE 
) 
 170             if ( !::CloseHandle(m_hFile
) ) 
 172                 wxLogSysError(_("Failed to close file handle")); 
 177     // return TRUE only if the file could be opened successfully 
 178     bool IsOk() const { return m_hFile 
!= INVALID_HANDLE_VALUE
; } 
 181     operator HANDLE() const { return m_hFile
; } 
 189 // ---------------------------------------------------------------------------- 
 191 // ---------------------------------------------------------------------------- 
 193 #if defined(__WIN32__) && !defined(__WXMICROWIN__) 
 195 // convert between wxDateTime and FILETIME which is a 64-bit value representing 
 196 // the number of 100-nanosecond intervals since January 1, 1601. 
 198 static void ConvertFileTimeToWx(wxDateTime 
*dt
, const FILETIME 
&ft
) 
 200     FILETIME ftcopy 
= ft
; 
 202     if ( !::FileTimeToLocalFileTime(&ftcopy
, &ftLocal
) ) 
 204         wxLogLastError(_T("FileTimeToLocalFileTime")); 
 208     if ( !::FileTimeToSystemTime(&ftLocal
, &st
) ) 
 210         wxLogLastError(_T("FileTimeToSystemTime")); 
 213     dt
->Set(st
.wDay
, wxDateTime::Month(st
.wMonth 
- 1), st
.wYear
, 
 214             st
.wHour
, st
.wMinute
, st
.wSecond
, st
.wMilliseconds
); 
 217 static void ConvertWxToFileTime(FILETIME 
*ft
, const wxDateTime
& dt
) 
 220     st
.wDay 
= dt
.GetDay(); 
 221     st
.wMonth 
= dt
.GetMonth() + 1; 
 222     st
.wYear 
= dt
.GetYear(); 
 223     st
.wHour 
= dt
.GetHour(); 
 224     st
.wMinute 
= dt
.GetMinute(); 
 225     st
.wSecond 
= dt
.GetSecond(); 
 226     st
.wMilliseconds 
= dt
.GetMillisecond(); 
 229     if ( !::SystemTimeToFileTime(&st
, &ftLocal
) ) 
 231         wxLogLastError(_T("SystemTimeToFileTime")); 
 234     if ( !::LocalFileTimeToFileTime(&ftLocal
, ft
) ) 
 236         wxLogLastError(_T("LocalFileTimeToFileTime")); 
 242 // return a string with the volume par 
 243 static wxString 
wxGetVolumeString(const wxString
& volume
, wxPathFormat format
) 
 247     if ( !volume
.empty() ) 
 249         format 
= wxFileName::GetFormat(format
); 
 251         // Special Windows UNC paths hack, part 2: undo what we did in 
 252         // SplitPath() and make an UNC path if we have a drive which is not a 
 253         // single letter (hopefully the network shares can't be one letter only 
 254         // although I didn't find any authoritative docs on this) 
 255         if ( format 
== wxPATH_DOS 
&& volume
.length() > 1 ) 
 257             path 
<< wxFILE_SEP_PATH_DOS 
<< wxFILE_SEP_PATH_DOS 
<< volume
; 
 259         else if  ( format 
== wxPATH_DOS 
|| format 
== wxPATH_VMS 
) 
 261             path 
<< volume 
<< wxFileName::GetVolumeSeparator(format
); 
 269 // ============================================================================ 
 271 // ============================================================================ 
 273 // ---------------------------------------------------------------------------- 
 274 // wxFileName construction 
 275 // ---------------------------------------------------------------------------- 
 277 void wxFileName::Assign( const wxFileName 
&filepath 
) 
 279     m_volume 
= filepath
.GetVolume(); 
 280     m_dirs 
= filepath
.GetDirs(); 
 281     m_name 
= filepath
.GetName(); 
 282     m_ext 
= filepath
.GetExt(); 
 283     m_relative 
= filepath
.m_relative
; 
 286 void wxFileName::Assign(const wxString
& volume
, 
 287                         const wxString
& path
, 
 288                         const wxString
& name
, 
 290                         wxPathFormat format 
) 
 292     SetPath( path
, format 
); 
 299 void wxFileName::SetPath( const wxString 
&path
, wxPathFormat format 
) 
 305         wxPathFormat my_format 
= GetFormat( format 
); 
 306         wxString my_path 
= path
; 
 308         // 1) Determine if the path is relative or absolute. 
 309         wxChar leadingChar 
= my_path
[0u]; 
 314                 m_relative 
= leadingChar 
== wxT(':'); 
 316                 // We then remove a leading ":". The reason is in our 
 317                 // storage form for relative paths: 
 318                 // ":dir:file.txt" actually means "./dir/file.txt" in 
 319                 // DOS notation and should get stored as 
 320                 // (relative) (dir) (file.txt) 
 321                 // "::dir:file.txt" actually means "../dir/file.txt" 
 322                 // stored as (relative) (..) (dir) (file.txt) 
 323                 // This is important only for the Mac as an empty dir 
 324                 // actually means <UP>, whereas under DOS, double 
 325                 // slashes can be ignored: "\\\\" is the same as "\\". 
 327                     my_path
.erase( 0, 1 ); 
 331                 // TODO: what is the relative path format here? 
 336                 // the paths of the form "~" or "~username" are absolute 
 337                 m_relative 
= leadingChar 
!= wxT('/') && leadingChar 
!= _T('~'); 
 341                 m_relative 
= !IsPathSeparator(leadingChar
, my_format
); 
 345                 wxFAIL_MSG( wxT("error") ); 
 349         // 2) Break up the path into its members. If the original path 
 350         //    was just "/" or "\\", m_dirs will be empty. We know from 
 351         //    the m_relative field, if this means "nothing" or "root dir". 
 353         wxStringTokenizer 
tn( my_path
, GetPathSeparators(my_format
) ); 
 355         while ( tn
.HasMoreTokens() ) 
 357             wxString token 
= tn
.GetNextToken(); 
 359             // Remove empty token under DOS and Unix, interpret them 
 363                 if (my_format 
== wxPATH_MAC
) 
 364                     m_dirs
.Add( wxT("..") ); 
 373     else // no path at all 
 379 void wxFileName::Assign(const wxString
& fullpath
, 
 382     wxString volume
, path
, name
, ext
; 
 383     SplitPath(fullpath
, &volume
, &path
, &name
, &ext
, format
); 
 385     Assign(volume
, path
, name
, ext
, format
); 
 388 void wxFileName::Assign(const wxString
& fullpathOrig
, 
 389                         const wxString
& fullname
, 
 392     // always recognize fullpath as directory, even if it doesn't end with a 
 394     wxString fullpath 
= fullpathOrig
; 
 395     if ( !wxEndsWithPathSeparator(fullpath
) ) 
 397         fullpath 
+= GetPathSeparator(format
); 
 400     wxString volume
, path
, name
, ext
; 
 402     // do some consistency checks in debug mode: the name should be really just 
 403     // the filename and the path should be really just a path 
 405     wxString pathDummy
, nameDummy
, extDummy
; 
 407     SplitPath(fullname
, &pathDummy
, &name
, &ext
, format
); 
 409     wxASSERT_MSG( pathDummy
.empty(), 
 410                   _T("the file name shouldn't contain the path") ); 
 412     SplitPath(fullpath
, &volume
, &path
, &nameDummy
, &extDummy
, format
); 
 414     wxASSERT_MSG( nameDummy
.empty() && extDummy
.empty(), 
 415                   _T("the path shouldn't contain file name nor extension") ); 
 417 #else // !__WXDEBUG__ 
 418     SplitPath(fullname
, NULL 
/* no path */, &name
, &ext
, format
); 
 419     SplitPath(fullpath
, &volume
, &path
, NULL
, NULL
, format
); 
 420 #endif // __WXDEBUG__/!__WXDEBUG__ 
 422     Assign(volume
, path
, name
, ext
, format
); 
 425 void wxFileName::AssignDir(const wxString
& dir
, wxPathFormat format
) 
 427     Assign(dir
, _T(""), format
); 
 430 void wxFileName::Clear() 
 436     m_ext 
= wxEmptyString
; 
 440 wxFileName 
wxFileName::FileName(const wxString
& file
) 
 442     return wxFileName(file
); 
 446 wxFileName 
wxFileName::DirName(const wxString
& dir
) 
 453 // ---------------------------------------------------------------------------- 
 455 // ---------------------------------------------------------------------------- 
 457 bool wxFileName::FileExists() 
 459     return wxFileName::FileExists( GetFullPath() ); 
 462 bool wxFileName::FileExists( const wxString 
&file 
) 
 464     return ::wxFileExists( file 
); 
 467 bool wxFileName::DirExists() 
 469     return wxFileName::DirExists( GetFullPath() ); 
 472 bool wxFileName::DirExists( const wxString 
&dir 
) 
 474     return ::wxDirExists( dir 
); 
 477 // ---------------------------------------------------------------------------- 
 478 // CWD and HOME stuff 
 479 // ---------------------------------------------------------------------------- 
 481 void wxFileName::AssignCwd(const wxString
& volume
) 
 483     AssignDir(wxFileName::GetCwd(volume
)); 
 487 wxString 
wxFileName::GetCwd(const wxString
& volume
) 
 489     // if we have the volume, we must get the current directory on this drive 
 490     // and to do this we have to chdir to this volume - at least under Windows, 
 491     // I don't know how to get the current drive on another volume elsewhere 
 494     if ( !volume
.empty() ) 
 497         SetCwd(volume 
+ GetVolumeSeparator()); 
 500     wxString cwd 
= ::wxGetCwd(); 
 502     if ( !volume
.empty() ) 
 510 bool wxFileName::SetCwd() 
 512     return wxFileName::SetCwd( GetFullPath() ); 
 515 bool wxFileName::SetCwd( const wxString 
&cwd 
) 
 517     return ::wxSetWorkingDirectory( cwd 
); 
 520 void wxFileName::AssignHomeDir() 
 522     AssignDir(wxFileName::GetHomeDir()); 
 525 wxString 
wxFileName::GetHomeDir() 
 527     return ::wxGetHomeDir(); 
 530 void wxFileName::AssignTempFileName(const wxString
& prefix
, wxFile 
*fileTemp
) 
 532     wxString tempname 
= CreateTempFileName(prefix
, fileTemp
); 
 533     if ( tempname
.empty() ) 
 535         // error, failed to get temp file name 
 546 wxFileName::CreateTempFileName(const wxString
& prefix
, wxFile 
*fileTemp
) 
 548     wxString path
, dir
, name
; 
 550     // use the directory specified by the prefix 
 551     SplitPath(prefix
, &dir
, &name
, NULL 
/* extension */); 
 553 #if defined(__WINDOWS__) && !defined(__WXMICROWIN__) 
 558         if ( !::GetTempPath(MAX_PATH
, wxStringBuffer(dir
, MAX_PATH 
+ 1)) ) 
 560             wxLogLastError(_T("GetTempPath")); 
 565             // GetTempFileName() fails if we pass it an empty string 
 569     else // we have a dir to create the file in 
 571         // ensure we use only the back slashes as GetTempFileName(), unlike all 
 572         // the other APIs, is picky and doesn't accept the forward ones 
 573         dir
.Replace(_T("/"), _T("\\")); 
 576     if ( !::GetTempFileName(dir
, name
, 0, wxStringBuffer(path
, MAX_PATH 
+ 1)) ) 
 578         wxLogLastError(_T("GetTempFileName")); 
 583     if ( !::GetTempFileName(NULL
, prefix
, 0, wxStringBuffer(path
, 1025)) ) 
 589 #elif defined(__WXPM__) 
 590     // for now just create a file 
 592     // future enhancements can be to set some extended attributes for file 
 593     // systems OS/2 supports that have them (HPFS, FAT32) and security 
 595     static const wxChar 
*szMktempSuffix 
= wxT("XXX"); 
 596     path 
<< dir 
<< _T('/') << name 
<< szMktempSuffix
; 
 598     // Temporarily remove - MN 
 600         ::DosCreateDir(wxStringBuffer(path
, MAX_PATH
), NULL
); 
 603 #else // !Windows, !OS/2 
 606 #if defined(__WXMAC__) && !defined(__DARWIN__) 
 607         dir 
= wxMacFindFolder(  (short) kOnSystemDisk
, kTemporaryFolderType
, kCreateFolder 
) ; 
 609         dir 
= wxGetenv(_T("TMP")); 
 612             dir 
= wxGetenv(_T("TEMP")); 
 629     if ( !wxEndsWithPathSeparator(dir
) && 
 630             (name
.empty() || !wxIsPathSeparator(name
[0u])) ) 
 632         path 
+= wxFILE_SEP_PATH
; 
 637 #if defined(HAVE_MKSTEMP) 
 638     // scratch space for mkstemp() 
 639     path 
+= _T("XXXXXX"); 
 641     // can use the cast here because the length doesn't change and the string 
 643     int fdTemp 
= mkstemp((char *)path
.mb_str()); 
 646         // this might be not necessary as mkstemp() on most systems should have 
 647         // already done it but it doesn't hurt neither... 
 650     else // mkstemp() succeeded 
 652         // avoid leaking the fd 
 655             fileTemp
->Attach(fdTemp
); 
 662 #else // !HAVE_MKSTEMP 
 666     path 
+= _T("XXXXXX"); 
 668     if ( !mktemp((char *)path
.mb_str()) ) 
 672 #else // !HAVE_MKTEMP (includes __DOS__) 
 673     // generate the unique file name ourselves 
 675     path 
<< (unsigned int)getpid(); 
 680     static const size_t numTries 
= 1000; 
 681     for ( size_t n 
= 0; n 
< numTries
; n
++ ) 
 683         // 3 hex digits is enough for numTries == 1000 < 4096 
 684         pathTry 
= path 
+ wxString::Format(_T("%.03x"), n
); 
 685         if ( !wxFile::Exists(pathTry
) ) 
 694 #endif // HAVE_MKTEMP/!HAVE_MKTEMP 
 699 #endif // HAVE_MKSTEMP/!HAVE_MKSTEMP 
 701 #endif // Windows/!Windows 
 705         wxLogSysError(_("Failed to create a temporary file name")); 
 707     else if ( fileTemp 
&& !fileTemp
->IsOpened() ) 
 709         // open the file - of course, there is a race condition here, this is 
 710         // why we always prefer using mkstemp()... 
 712         // NB: GetTempFileName() under Windows creates the file, so using 
 713         //     write_excl there would fail 
 714         if ( !fileTemp
->Open(path
, 
 715 #if defined(__WINDOWS__) && !defined(__WXMICROWIN__) 
 720                              wxS_IRUSR 
| wxS_IWUSR
) ) 
 722             // FIXME: If !ok here should we loop and try again with another 
 723             //        file name?  That is the standard recourse if open(O_EXCL) 
 724             //        fails, though of course it should be protected against 
 725             //        possible infinite looping too. 
 727             wxLogError(_("Failed to open temporary file.")); 
 736 // ---------------------------------------------------------------------------- 
 737 // directory operations 
 738 // ---------------------------------------------------------------------------- 
 740 bool wxFileName::Mkdir( int perm
, int flags 
) 
 742     return wxFileName::Mkdir( GetFullPath(), perm
, flags 
); 
 745 bool wxFileName::Mkdir( const wxString
& dir
, int perm
, int flags 
) 
 747     if ( flags 
& wxPATH_MKDIR_FULL 
) 
 749         // split the path in components 
 751         filename
.AssignDir(dir
); 
 754         if ( filename
.HasVolume()) 
 756             currPath 
<< wxGetVolumeString(filename
.GetVolume(), wxPATH_NATIVE
); 
 759         wxArrayString dirs 
= filename
.GetDirs(); 
 760         size_t count 
= dirs
.GetCount(); 
 761         for ( size_t i 
= 0; i 
< count
; i
++ ) 
 763             if ( i 
> 0 || filename
.IsAbsolute() ) 
 764                 currPath 
+= wxFILE_SEP_PATH
; 
 767             if (!DirExists(currPath
)) 
 769                 if (!wxMkdir(currPath
, perm
)) 
 771                     // no need to try creating further directories 
 781     return ::wxMkdir( dir
, perm 
); 
 784 bool wxFileName::Rmdir() 
 786     return wxFileName::Rmdir( GetFullPath() ); 
 789 bool wxFileName::Rmdir( const wxString 
&dir 
) 
 791     return ::wxRmdir( dir 
); 
 794 // ---------------------------------------------------------------------------- 
 795 // path normalization 
 796 // ---------------------------------------------------------------------------- 
 798 bool wxFileName::Normalize(int flags
, 
 802     // the existing path components 
 803     wxArrayString dirs 
= GetDirs(); 
 805     // the path to prepend in front to make the path absolute 
 808     format 
= GetFormat(format
); 
 810     // make the path absolute 
 811     if ( (flags 
& wxPATH_NORM_ABSOLUTE
) && !IsAbsolute(format
) ) 
 815             curDir
.AssignCwd(GetVolume()); 
 819             curDir
.AssignDir(cwd
); 
 822         // the path may be not absolute because it doesn't have the volume name 
 823         // but in this case we shouldn't modify the directory components of it 
 824         // but just set the current volume 
 825         if ( !HasVolume() && curDir
.HasVolume() ) 
 827             SetVolume(curDir
.GetVolume()); 
 831                 // yes, it was the case - we don't need curDir then 
 837     // handle ~ stuff under Unix only 
 838     if ( (format 
== wxPATH_UNIX
) && (flags 
& wxPATH_NORM_TILDE
) ) 
 840         if ( !dirs
.IsEmpty() ) 
 842             wxString dir 
= dirs
[0u]; 
 843             if ( !dir
.empty() && dir
[0u] == _T('~') ) 
 845                 curDir
.AssignDir(wxGetUserHome(dir
.c_str() + 1)); 
 852     // transform relative path into abs one 
 855         wxArrayString dirsNew 
= curDir
.GetDirs(); 
 856         size_t count 
= dirs
.GetCount(); 
 857         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 859             dirsNew
.Add(dirs
[n
]); 
 865     // now deal with ".", ".." and the rest 
 867     size_t count 
= dirs
.GetCount(); 
 868     for ( size_t n 
= 0; n 
< count
; n
++ ) 
 870         wxString dir 
= dirs
[n
]; 
 872         if ( flags 
& wxPATH_NORM_DOTS 
) 
 874             if ( dir 
== wxT(".") ) 
 880             if ( dir 
== wxT("..") ) 
 882                 if ( m_dirs
.IsEmpty() ) 
 884                     wxLogError(_("The path '%s' contains too many \"..\"!"), 
 885                                GetFullPath().c_str()); 
 889                 m_dirs
.RemoveAt(m_dirs
.GetCount() - 1); 
 894         if ( flags 
& wxPATH_NORM_ENV_VARS 
) 
 896             dir 
= wxExpandEnvVars(dir
); 
 899         if ( (flags 
& wxPATH_NORM_CASE
) && !IsCaseSensitive(format
) ) 
 907     if ( (flags 
& wxPATH_NORM_CASE
) && !IsCaseSensitive(format
) ) 
 909         // VZ: expand env vars here too? 
 915 #if defined(__WIN32__) 
 916     if ( (flags 
& wxPATH_NORM_LONG
) && (format 
== wxPATH_DOS
) ) 
 918         Assign(GetLongPath()); 
 922     // we do have the path now 
 928 // ---------------------------------------------------------------------------- 
 929 // absolute/relative paths 
 930 // ---------------------------------------------------------------------------- 
 932 bool wxFileName::IsAbsolute(wxPathFormat format
) const 
 934     // if our path doesn't start with a path separator, it's not an absolute 
 939     if ( !GetVolumeSeparator(format
).empty() ) 
 941         // this format has volumes and an absolute path must have one, it's not 
 942         // enough to have the full path to bean absolute file under Windows 
 943         if ( GetVolume().empty() ) 
 950 bool wxFileName::MakeRelativeTo(const wxString
& pathBase
, wxPathFormat format
) 
 952     wxFileName 
fnBase(pathBase
, format
); 
 954     // get cwd only once - small time saving 
 955     wxString cwd 
= wxGetCwd(); 
 956     Normalize(wxPATH_NORM_ALL
, cwd
, format
); 
 957     fnBase
.Normalize(wxPATH_NORM_ALL
, cwd
, format
); 
 959     bool withCase 
= IsCaseSensitive(format
); 
 961     // we can't do anything if the files live on different volumes 
 962     if ( !GetVolume().IsSameAs(fnBase
.GetVolume(), withCase
) ) 
 968     // same drive, so we don't need our volume 
 971     // remove common directories starting at the top 
 972     while ( !m_dirs
.IsEmpty() && !fnBase
.m_dirs
.IsEmpty() && 
 973                 m_dirs
[0u].IsSameAs(fnBase
.m_dirs
[0u], withCase
) ) 
 976         fnBase
.m_dirs
.RemoveAt(0); 
 979     // add as many ".." as needed 
 980     size_t count 
= fnBase
.m_dirs
.GetCount(); 
 981     for ( size_t i 
= 0; i 
< count
; i
++ ) 
 983         m_dirs
.Insert(wxT(".."), 0u); 
 986     if ( format 
== wxPATH_UNIX 
|| format 
== wxPATH_DOS 
) 
 988         // a directory made relative with respect to itself is '.' under Unix 
 989         // and DOS, by definition (but we don't have to insert "./" for the 
 991         if ( m_dirs
.IsEmpty() && IsDir() ) 
1003 // ---------------------------------------------------------------------------- 
1004 // filename kind tests 
1005 // ---------------------------------------------------------------------------- 
1007 bool wxFileName::SameAs(const wxFileName 
&filepath
, wxPathFormat format
) 
1009     wxFileName fn1 
= *this, 
1012     // get cwd only once - small time saving 
1013     wxString cwd 
= wxGetCwd(); 
1014     fn1
.Normalize(wxPATH_NORM_ALL
, cwd
, format
); 
1015     fn2
.Normalize(wxPATH_NORM_ALL
, cwd
, format
); 
1017     if ( fn1
.GetFullPath() == fn2
.GetFullPath() ) 
1020     // TODO: compare inodes for Unix, this works even when filenames are 
1021     //       different but files are the same (symlinks) (VZ) 
1027 bool wxFileName::IsCaseSensitive( wxPathFormat format 
) 
1029     // only Unix filenames are truely case-sensitive 
1030     return GetFormat(format
) == wxPATH_UNIX
; 
1034 wxString 
wxFileName::GetVolumeSeparator(wxPathFormat format
) 
1038     if ( (GetFormat(format
) == wxPATH_DOS
) || 
1039          (GetFormat(format
) == wxPATH_VMS
) ) 
1041         sepVol 
= wxFILE_SEP_DSK
; 
1049 wxString 
wxFileName::GetPathSeparators(wxPathFormat format
) 
1052     switch ( GetFormat(format
) ) 
1055             // accept both as native APIs do but put the native one first as 
1056             // this is the one we use in GetFullPath() 
1057             seps 
<< wxFILE_SEP_PATH_DOS 
<< wxFILE_SEP_PATH_UNIX
; 
1061             wxFAIL_MSG( _T("unknown wxPATH_XXX style") ); 
1065             seps 
= wxFILE_SEP_PATH_UNIX
; 
1069             seps 
= wxFILE_SEP_PATH_MAC
; 
1073             seps 
= wxFILE_SEP_PATH_VMS
; 
1081 bool wxFileName::IsPathSeparator(wxChar ch
, wxPathFormat format
) 
1083     // wxString::Find() doesn't work as expected with NUL - it will always find 
1084     // it, so it is almost surely a bug if this function is called with NUL arg 
1085     wxASSERT_MSG( ch 
!= _T('\0'), _T("shouldn't be called with NUL") ); 
1087     return GetPathSeparators(format
).Find(ch
) != wxNOT_FOUND
; 
1090 // ---------------------------------------------------------------------------- 
1091 // path components manipulation 
1092 // ---------------------------------------------------------------------------- 
1094 void wxFileName::AppendDir( const wxString 
&dir 
) 
1099 void wxFileName::PrependDir( const wxString 
&dir 
) 
1101     m_dirs
.Insert( dir
, 0 ); 
1104 void wxFileName::InsertDir( int before
, const wxString 
&dir 
) 
1106     m_dirs
.Insert( dir
, before 
); 
1109 void wxFileName::RemoveDir( int pos 
) 
1111     m_dirs
.Remove( (size_t)pos 
); 
1114 // ---------------------------------------------------------------------------- 
1116 // ---------------------------------------------------------------------------- 
1118 void wxFileName::SetFullName(const wxString
& fullname
) 
1120     SplitPath(fullname
, NULL 
/* no path */, &m_name
, &m_ext
); 
1123 wxString 
wxFileName::GetFullName() const 
1125     wxString fullname 
= m_name
; 
1126     if ( !m_ext
.empty() ) 
1128         fullname 
<< wxFILE_SEP_EXT 
<< m_ext
; 
1134 wxString 
wxFileName::GetPath( int flags
, wxPathFormat format 
) const 
1136     format 
= GetFormat( format 
); 
1140     // return the volume with the path as well if requested 
1141     if ( flags 
& wxPATH_GET_VOLUME 
) 
1143         fullpath 
+= wxGetVolumeString(GetVolume(), format
); 
1146     // the leading character 
1147     if ( format 
== wxPATH_MAC 
) 
1150             fullpath 
+= wxFILE_SEP_PATH_MAC
; 
1152     else if ( format 
== wxPATH_DOS 
) 
1155             fullpath 
+= wxFILE_SEP_PATH_DOS
; 
1157     else if ( format 
== wxPATH_UNIX 
) 
1161             // normally the absolute file names starts with a slash with one 
1162             // exception: file names like "~/foo.bar" don't have it 
1163             if ( m_dirs
.IsEmpty() || m_dirs
[0u] != _T('~') ) 
1165                 fullpath 
+= wxFILE_SEP_PATH_UNIX
; 
1170     // then concatenate all the path components using the path separator 
1171     size_t dirCount 
= m_dirs
.GetCount(); 
1174         if ( format 
== wxPATH_VMS 
) 
1176             fullpath 
+= wxT('['); 
1179         for ( size_t i 
= 0; i 
< dirCount
; i
++ ) 
1184                     if ( m_dirs
[i
] == wxT(".") ) 
1186                         // skip appending ':', this shouldn't be done in this 
1187                         // case as "::" is interpreted as ".." under Unix 
1191                     // convert back from ".." to nothing 
1192                     if ( m_dirs
[i
] != wxT("..") ) 
1193                          fullpath 
+= m_dirs
[i
]; 
1197                     wxFAIL_MSG( wxT("unexpected path format") ); 
1198                     // still fall through 
1202                     fullpath 
+= m_dirs
[i
]; 
1206                     // TODO: What to do with ".." under VMS 
1207                     // convert back from ".." to nothing 
1208                     if ( m_dirs
[i
] != wxT("..") ) 
1209                         fullpath 
+= m_dirs
[i
]; 
1213             if ( i 
!= dirCount 
- 1 ) 
1214                 fullpath 
+= GetPathSeparator(format
); 
1217         if ( format 
== wxPATH_VMS 
) 
1219             fullpath 
+= wxT(']'); 
1223     if ( (flags 
& wxPATH_GET_SEPARATOR
) && !fullpath
.empty() ) 
1225         fullpath 
+= GetPathSeparator(format
); 
1231 wxString 
wxFileName::GetFullPath( wxPathFormat format 
) const 
1233     // we already have a function to get the path 
1234     wxString fullpath 
= GetPath(wxPATH_GET_VOLUME 
| wxPATH_GET_SEPARATOR
, 
1237     // now just add the file name and extension to it 
1238     fullpath 
+= GetFullName(); 
1243 // Return the short form of the path (returns identity on non-Windows platforms) 
1244 wxString 
wxFileName::GetShortPath() const 
1246 #if defined(__WXMSW__) && defined(__WIN32__) && !defined(__WXMICROWIN__) 
1247     wxString 
path(GetFullPath()); 
1249     DWORD sz 
= ::GetShortPathName(path
, NULL
, 0); 
1253         ok 
= ::GetShortPathName
 
1256                 pathOut
.GetWriteBuf(sz
), 
1259         pathOut
.UngetWriteBuf(); 
1266     return GetFullPath(); 
1270 // Return the long form of the path (returns identity on non-Windows platforms) 
1271 wxString 
wxFileName::GetLongPath() const 
1274              path 
= GetFullPath(); 
1276 #if defined(__WIN32__) && !defined(__WXMICROWIN__) 
1277     bool success 
= FALSE
; 
1279 #if wxUSE_DYNAMIC_LOADER 
1280     typedef DWORD (WINAPI 
*GET_LONG_PATH_NAME
)(const wxChar 
*, wxChar 
*, DWORD
); 
1282     static bool s_triedToLoad 
= FALSE
; 
1284     if ( !s_triedToLoad 
) 
1286         s_triedToLoad 
= TRUE
; 
1287         wxDynamicLibrary 
dllKernel(_T("kernel32")); 
1288         if ( dllKernel
.IsLoaded() ) 
1290             // may succeed or fail depending on the Windows version 
1291             static GET_LONG_PATH_NAME s_pfnGetLongPathName 
= NULL
; 
1293             s_pfnGetLongPathName 
= (GET_LONG_PATH_NAME
) dllKernel
.GetSymbol(_T("GetLongPathNameW")); 
1295             s_pfnGetLongPathName 
= (GET_LONG_PATH_NAME
) dllKernel
.GetSymbol(_T("GetLongPathNameA")); 
1298             if ( s_pfnGetLongPathName 
) 
1300                 DWORD dwSize 
= (*s_pfnGetLongPathName
)(path
, NULL
, 0); 
1301                 bool ok 
= dwSize 
> 0; 
1305                     DWORD sz 
= (*s_pfnGetLongPathName
)(path
, NULL
, 0); 
1309                         ok 
= (*s_pfnGetLongPathName
) 
1312                                 pathOut
.GetWriteBuf(sz
), 
1315                         pathOut
.UngetWriteBuf(); 
1325 #endif // wxUSE_DYNAMIC_LOADER 
1329         // The OS didn't support GetLongPathName, or some other error. 
1330         // We need to call FindFirstFile on each component in turn. 
1332         WIN32_FIND_DATA findFileData
; 
1334         pathOut 
= wxEmptyString
; 
1336         wxArrayString dirs 
= GetDirs(); 
1337         dirs
.Add(GetFullName()); 
1341         size_t count 
= dirs
.GetCount(); 
1342         for ( size_t i 
= 0; i 
< count
; i
++ ) 
1344             // We're using pathOut to collect the long-name path, but using a 
1345             // temporary for appending the last path component which may be 
1347             tmpPath 
= pathOut 
+ dirs
[i
]; 
1349             if ( tmpPath
.empty() ) 
1352             if ( tmpPath
.Last() == wxT(':') ) 
1354                 // Can't pass a drive and root dir to FindFirstFile, 
1355                 // so continue to next dir 
1356                 tmpPath 
+= wxFILE_SEP_PATH
; 
1361             hFind 
= ::FindFirstFile(tmpPath
, &findFileData
); 
1362             if (hFind 
== INVALID_HANDLE_VALUE
) 
1364                 // Error: return immediately with the original path 
1368             pathOut 
+= findFileData
.cFileName
; 
1369             if ( (i 
< (count
-1)) ) 
1370                 pathOut 
+= wxFILE_SEP_PATH
; 
1377 #endif // Win32/!Win32 
1382 wxPathFormat 
wxFileName::GetFormat( wxPathFormat format 
) 
1384     if (format 
== wxPATH_NATIVE
) 
1386 #if defined(__WXMSW__) || defined(__WXPM__) || defined(__DOS__) 
1387         format 
= wxPATH_DOS
; 
1388 #elif defined(__WXMAC__) && !defined(__DARWIN__) 
1389         format 
= wxPATH_MAC
; 
1390 #elif defined(__VMS) 
1391         format 
= wxPATH_VMS
; 
1393         format 
= wxPATH_UNIX
; 
1399 // ---------------------------------------------------------------------------- 
1400 // path splitting function 
1401 // ---------------------------------------------------------------------------- 
1404 void wxFileName::SplitPath(const wxString
& fullpathWithVolume
, 
1405                            wxString 
*pstrVolume
, 
1409                            wxPathFormat format
) 
1411     format 
= GetFormat(format
); 
1413     wxString fullpath 
= fullpathWithVolume
; 
1415     // under VMS the end of the path is ']', not the path separator used to 
1416     // separate the components 
1417     wxString sepPath 
= format 
== wxPATH_VMS 
? wxString(_T(']')) 
1418                                             : GetPathSeparators(format
); 
1420     // special Windows UNC paths hack: transform \\share\path into share:path 
1421     if ( format 
== wxPATH_DOS 
) 
1423         if ( fullpath
.length() >= 4 && 
1424                 fullpath
[0u] == wxFILE_SEP_PATH_DOS 
&& 
1425                     fullpath
[1u] == wxFILE_SEP_PATH_DOS 
) 
1427             fullpath
.erase(0, 2); 
1429             size_t posFirstSlash 
= fullpath
.find_first_of(sepPath
); 
1430             if ( posFirstSlash 
!= wxString::npos 
) 
1432                 fullpath
[posFirstSlash
] = wxFILE_SEP_DSK
; 
1434                 // UNC paths are always absolute, right? (FIXME) 
1435                 fullpath
.insert(posFirstSlash 
+ 1, wxFILE_SEP_PATH_DOS
); 
1440     // We separate the volume here 
1441     if ( format 
== wxPATH_DOS 
|| format 
== wxPATH_VMS 
) 
1443         wxString sepVol 
= GetVolumeSeparator(format
); 
1445         size_t posFirstColon 
= fullpath
.find_first_of(sepVol
); 
1446         if ( posFirstColon 
!= wxString::npos 
) 
1450                 *pstrVolume 
= fullpath
.Left(posFirstColon
); 
1453             // remove the volume name and the separator from the full path 
1454             fullpath
.erase(0, posFirstColon 
+ sepVol
.length()); 
1458     // find the positions of the last dot and last path separator in the path 
1459     size_t posLastDot 
= fullpath
.find_last_of(wxFILE_SEP_EXT
); 
1460     size_t posLastSlash 
= fullpath
.find_last_of(sepPath
); 
1462     if ( (posLastDot 
!= wxString::npos
) && 
1463             ((format 
== wxPATH_UNIX
) || (format 
== wxPATH_VMS
)) ) 
1465         if ( (posLastDot 
== 0) || 
1466              (fullpath
[posLastDot 
- 1] == sepPath
[0u] ) ) 
1468             // under Unix and VMS, dot may be (and commonly is) the first 
1469             // character of the filename, don't treat the entire filename as 
1470             // extension in this case 
1471             posLastDot 
= wxString::npos
; 
1475     // if we do have a dot and a slash, check that the dot is in the name part 
1476     if ( (posLastDot 
!= wxString::npos
) && 
1477          (posLastSlash 
!= wxString::npos
) && 
1478          (posLastDot 
< posLastSlash
) ) 
1480         // the dot is part of the path, not the start of the extension 
1481         posLastDot 
= wxString::npos
; 
1484     // now fill in the variables provided by user 
1487         if ( posLastSlash 
== wxString::npos 
) 
1494             // take everything up to the path separator but take care to make 
1495             // the path equal to something like '/', not empty, for the files 
1496             // immediately under root directory 
1497             size_t len 
= posLastSlash
; 
1499             // this rule does not apply to mac since we do not start with colons (sep) 
1500             // except for relative paths 
1501             if ( !len 
&& format 
!= wxPATH_MAC
) 
1504             *pstrPath 
= fullpath
.Left(len
); 
1506             // special VMS hack: remove the initial bracket 
1507             if ( format 
== wxPATH_VMS 
) 
1509                 if ( (*pstrPath
)[0u] == _T('[') ) 
1510                     pstrPath
->erase(0, 1); 
1517         // take all characters starting from the one after the last slash and 
1518         // up to, but excluding, the last dot 
1519         size_t nStart 
= posLastSlash 
== wxString::npos 
? 0 : posLastSlash 
+ 1; 
1521         if ( posLastDot 
== wxString::npos 
) 
1523             // take all until the end 
1524             count 
= wxString::npos
; 
1526         else if ( posLastSlash 
== wxString::npos 
) 
1530         else // have both dot and slash 
1532             count 
= posLastDot 
- posLastSlash 
- 1; 
1535         *pstrName 
= fullpath
.Mid(nStart
, count
); 
1540         if ( posLastDot 
== wxString::npos 
) 
1547             // take everything after the dot 
1548             *pstrExt 
= fullpath
.Mid(posLastDot 
+ 1); 
1554 void wxFileName::SplitPath(const wxString
& fullpath
, 
1558                            wxPathFormat format
) 
1561     SplitPath(fullpath
, &volume
, path
, name
, ext
, format
); 
1565         path
->Prepend(wxGetVolumeString(volume
, format
)); 
1569 // ---------------------------------------------------------------------------- 
1571 // ---------------------------------------------------------------------------- 
1573 bool wxFileName::SetTimes(const wxDateTime 
*dtAccess
, 
1574                           const wxDateTime 
*dtMod
, 
1575                           const wxDateTime 
*dtCreate
) 
1577 #if defined(__UNIX_LIKE__) || (defined(__DOS__) && defined(__WATCOMC__)) 
1578     if ( !dtAccess 
&& !dtMod 
) 
1580         // can't modify the creation time anyhow, don't try 
1584     // if dtAccess or dtMod is not specified, use the other one (which must be 
1585     // non NULL because of the test above) for both times 
1587     utm
.actime 
= dtAccess 
? dtAccess
->GetTicks() : dtMod
->GetTicks(); 
1588     utm
.modtime 
= dtMod 
? dtMod
->GetTicks() : dtAccess
->GetTicks(); 
1589     if ( utime(GetFullPath(), &utm
) == 0 ) 
1593 #elif defined(__WIN32__) 
1594     wxFileHandle 
fh(GetFullPath(), wxFileHandle::Write
); 
1597         FILETIME ftAccess
, ftCreate
, ftWrite
; 
1600             ConvertWxToFileTime(&ftCreate
, *dtCreate
); 
1602             ConvertWxToFileTime(&ftAccess
, *dtAccess
); 
1604             ConvertWxToFileTime(&ftWrite
, *dtMod
); 
1606         if ( ::SetFileTime(fh
, 
1607                            dtCreate 
? &ftCreate 
: NULL
, 
1608                            dtAccess 
? &ftAccess 
: NULL
, 
1609                            dtMod 
? &ftWrite 
: NULL
) ) 
1614 #else // other platform 
1617     wxLogSysError(_("Failed to modify file times for '%s'"), 
1618                   GetFullPath().c_str()); 
1623 bool wxFileName::Touch() 
1625 #if defined(__UNIX_LIKE__) 
1626     // under Unix touching file is simple: just pass NULL to utime() 
1627     if ( utime(GetFullPath(), NULL
) == 0 ) 
1632     wxLogSysError(_("Failed to touch the file '%s'"), GetFullPath().c_str()); 
1635 #else // other platform 
1636     wxDateTime dtNow 
= wxDateTime::Now(); 
1638     return SetTimes(&dtNow
, &dtNow
, NULL 
/* don't change create time */); 
1642 bool wxFileName::GetTimes(wxDateTime 
*dtAccess
, 
1644                           wxDateTime 
*dtCreate
) const 
1646 #if defined(__UNIX_LIKE__) || defined(__WXMAC__) || (defined(__DOS__) && defined(__WATCOMC__)) 
1648     if ( wxStat(GetFullPath(), &stBuf
) == 0 ) 
1651             dtAccess
->Set(stBuf
.st_atime
); 
1653             dtMod
->Set(stBuf
.st_mtime
); 
1655             dtCreate
->Set(stBuf
.st_ctime
); 
1659 #elif defined(__WIN32__) 
1660     wxFileHandle 
fh(GetFullPath(), wxFileHandle::Read
); 
1663         FILETIME ftAccess
, ftCreate
, ftWrite
; 
1665         if ( ::GetFileTime(fh
, 
1666                            dtMod 
? &ftCreate 
: NULL
, 
1667                            dtAccess 
? &ftAccess 
: NULL
, 
1668                            dtCreate 
? &ftWrite 
: NULL
) ) 
1671                 ConvertFileTimeToWx(dtMod
, ftCreate
); 
1673                 ConvertFileTimeToWx(dtAccess
, ftAccess
); 
1675                 ConvertFileTimeToWx(dtCreate
, ftWrite
); 
1680 #else // other platform 
1683     wxLogSysError(_("Failed to retrieve file times for '%s'"), 
1684                   GetFullPath().c_str()); 
1691 const short kMacExtensionMaxLength 
= 16 ; 
1694   char m_ext
[kMacExtensionMaxLength
] ; 
1697 } MacDefaultExtensionRecord 
; 
1699 #include "wx/dynarray.h" 
1700 WX_DECLARE_OBJARRAY(MacDefaultExtensionRecord
, MacDefaultExtensionArray
) ; 
1701 #include "wx/arrimpl.cpp" 
1702 WX_DEFINE_OBJARRAY(MacDefaultExtensionArray
) ; 
1704 MacDefaultExtensionArray gMacDefaultExtensions 
; 
1705 bool gMacDefaultExtensionsInited 
= false ; 
1707 static void MacEnsureDefaultExtensionsLoaded() 
1709   if ( !gMacDefaultExtensionsInited 
) 
1711     // load the default extensions 
1712     MacDefaultExtensionRecord defaults
[] = 
1714       { "txt" , 'TEXT' , 'ttxt' } , 
1717     // we could load the pc exchange prefs here too 
1719     for ( int i 
= 0 ; i 
< WXSIZEOF( defaults 
) ; ++i 
) 
1721       gMacDefaultExtensions
.Add( defaults
[i
] ) ; 
1723     gMacDefaultExtensionsInited 
= true ; 
1726 bool wxFileName::MacSetTypeAndCreator( wxUint32 type 
, wxUint32 creator 
) 
1730   wxMacFilename2FSSpec(GetFullPath(),&spec
) ; 
1731   OSErr err 
= FSpGetFInfo( &spec 
, &fndrInfo 
) ; 
1732   wxCHECK( err 
== noErr 
, false ) ; 
1734   fndrInfo
.fdType 
= type 
; 
1735   fndrInfo
.fdCreator 
= creator 
; 
1736   FSpSetFInfo( &spec 
, &fndrInfo 
) ; 
1740 bool wxFileName::MacGetTypeAndCreator( wxUint32 
*type 
, wxUint32 
*creator 
) 
1744   wxMacFilename2FSSpec(GetFullPath(),&spec
) ; 
1745   OSErr err 
= FSpGetFInfo( &spec 
, &fndrInfo 
) ; 
1746   wxCHECK( err 
== noErr 
, false ) ; 
1748   *type 
= fndrInfo
.fdType 
; 
1749   *creator 
= fndrInfo
.fdCreator 
; 
1753 bool wxFileName::MacSetDefaultTypeAndCreator() 
1755     wxUint32 type 
, creator 
; 
1756     if ( wxFileName::MacFindDefaultTypeAndCreator(GetExt() , &type 
, 
1759         return MacSetTypeAndCreator( type 
, creator 
) ; 
1764 bool wxFileName::MacFindDefaultTypeAndCreator( const wxString
& ext 
, wxUint32 
*type 
, wxUint32 
*creator 
) 
1766   MacEnsureDefaultExtensionsLoaded() ; 
1767   wxString extl 
= ext
.Lower() ; 
1768   for( int i 
= gMacDefaultExtensions
.Count() - 1 ; i 
>= 0 ; --i 
) 
1770     if ( gMacDefaultExtensions
.Item(i
).m_ext 
== extl 
) 
1772       *type 
= gMacDefaultExtensions
.Item(i
).m_type 
; 
1773       *creator 
= gMacDefaultExtensions
.Item(i
).m_creator 
; 
1780 void wxFileName::MacRegisterDefaultTypeAndCreator( const wxString
& ext 
, wxUint32 type 
, wxUint32 creator 
) 
1782   MacEnsureDefaultExtensionsLoaded() ; 
1783   MacDefaultExtensionRecord rec 
; 
1785   rec
.m_creator 
= creator 
; 
1786   strncpy( rec
.m_ext 
, ext
.Lower().c_str() , kMacExtensionMaxLength 
) ; 
1787   gMacDefaultExtensions
.Add( rec 
) ;