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 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  21     #pragma implementation "filename.h" 
  24 // For compilers that support precompilation, includes "wx.h". 
  25 #include "wx/wxprec.h" 
  36 #include "wx/filename.h" 
  37 #include "wx/tokenzr.h" 
  38 #include "wx/config.h"          // for wxExpandEnvVars 
  41 #if wxUSE_DYNLIB_CLASS 
  42 #include "wx/dynlib.h" 
  45 // For GetShort/LongPathName 
  48 #include "wx/msw/winundef.h" 
  51 // ============================================================================ 
  53 // ============================================================================ 
  55 // ---------------------------------------------------------------------------- 
  56 // wxFileName construction 
  57 // ---------------------------------------------------------------------------- 
  59 void wxFileName::Assign( const wxFileName 
&filepath 
) 
  61     m_ext 
= filepath
.GetExt(); 
  62     m_name 
= filepath
.GetName(); 
  63     m_dirs 
= filepath
.GetDirs(); 
  66 void wxFileName::Assign( const wxString
& path
, 
  71     wxStringTokenizer 
tn(path
, GetPathSeparators(format
), 
  72                          wxTOKEN_RET_EMPTY_ALL
); 
  75     while ( tn
.HasMoreTokens() ) 
  77         wxString token 
= tn
.GetNextToken(); 
  79         // If the path starts with a slash, we need the first 
  80         // dir entry to be an empty for later reassembly. 
  81         if (first 
|| !token
.IsEmpty()) 
  91 void wxFileName::Assign(const wxString
& fullpath
, 
  94     wxString path
, name
, ext
; 
  95     SplitPath(fullpath
, &path
, &name
, &ext
, format
); 
  97     Assign(path
, name
, ext
, format
); 
 100 void wxFileName::Assign(const wxString
& path
, 
 101                         const wxString
& fullname
, 
 105     SplitPath(fullname
, NULL 
/* no path */, &name
, &ext
, format
); 
 107     Assign(path
, name
, ext
, format
); 
 110 void wxFileName::Clear() 
 114     m_ext 
= wxEmptyString
; 
 118 wxFileName 
wxFileName::FileName(const wxString
& file
) 
 120     return wxFileName(file
); 
 124 wxFileName 
wxFileName::DirName(const wxString
& dir
) 
 131 // ---------------------------------------------------------------------------- 
 133 // ---------------------------------------------------------------------------- 
 135 bool wxFileName::FileExists() 
 137     return wxFileName::FileExists( GetFullPath() ); 
 140 bool wxFileName::FileExists( const wxString 
&file 
) 
 142     return ::wxFileExists( file 
); 
 145 bool wxFileName::DirExists() 
 147     return wxFileName::DirExists( GetFullPath() ); 
 150 bool wxFileName::DirExists( const wxString 
&dir 
) 
 152     return ::wxDirExists( dir 
); 
 155 wxDateTime 
wxFileName::GetModificationTime() 
 157     wxDateTime 
ret( wxFileModificationTime( GetFullPath() ) ); 
 162 // ---------------------------------------------------------------------------- 
 163 // CWD and HOME stuff 
 164 // ---------------------------------------------------------------------------- 
 166 void wxFileName::AssignCwd() 
 168     AssignDir(wxFileName::GetCwd()); 
 172 wxString 
wxFileName::GetCwd() 
 177 bool wxFileName::SetCwd() 
 179     return wxFileName::SetCwd( GetFullPath() ); 
 182 bool wxFileName::SetCwd( const wxString 
&cwd 
) 
 184     return ::wxSetWorkingDirectory( cwd 
); 
 187 void wxFileName::AssignHomeDir() 
 189     AssignDir(wxFileName::GetHomeDir()); 
 192 wxString 
wxFileName::GetHomeDir() 
 194     return ::wxGetHomeDir(); 
 197 void wxFileName::AssignTempFileName( const wxString 
&prefix 
) 
 200     if ( wxGetTempFileName(prefix
, fullname
) ) 
 210 // ---------------------------------------------------------------------------- 
 211 // directory operations 
 212 // ---------------------------------------------------------------------------- 
 214 bool wxFileName::Mkdir( int perm
, bool full 
) 
 216     return wxFileName::Mkdir( GetFullPath(), perm
, full 
); 
 219 bool wxFileName::Mkdir( const wxString 
&dir
, int perm
, bool full 
) 
 223         wxFileName 
filename(dir
); 
 224         wxArrayString dirs 
= filename
.GetDirs(); 
 225         dirs
.Add(filename
.GetName()); 
 227         size_t count 
= dirs
.GetCount(); 
 231         for ( i 
= 0; i 
< count
; i
++ ) 
 235             if (currPath
.Last() == wxT(':')) 
 237                 // Can't create a root directory so continue to next dir 
 238                 currPath 
+= wxFILE_SEP_PATH
; 
 242             if (!DirExists(currPath
)) 
 243                 if (!wxMkdir(currPath
, perm
)) 
 246             if ( (i 
< (count
-1)) ) 
 247                 currPath 
+= wxFILE_SEP_PATH
; 
 250         return (noErrors 
== 0); 
 254         return ::wxMkdir( dir
, perm 
); 
 257 bool wxFileName::Rmdir() 
 259     return wxFileName::Rmdir( GetFullPath() ); 
 262 bool wxFileName::Rmdir( const wxString 
&dir 
) 
 264     return ::wxRmdir( dir 
); 
 267 // ---------------------------------------------------------------------------- 
 268 // path normalization 
 269 // ---------------------------------------------------------------------------- 
 271 bool wxFileName::Normalize(wxPathNormalize flags
, 
 275     // the existing path components 
 276     wxArrayString dirs 
= GetDirs(); 
 278     // the path to prepend in front to make the path absolute 
 281     format 
= GetFormat(format
); 
 283     // make the path absolute 
 284     if ( (flags 
& wxPATH_NORM_ABSOLUTE
) && !IsAbsolute() ) 
 289             curDir
.AssignDir(cwd
); 
 292     // handle ~ stuff under Unix only 
 293     if ( (format 
== wxPATH_UNIX
) && (flags 
& wxPATH_NORM_TILDE
) ) 
 295         if ( !dirs
.IsEmpty() ) 
 297             wxString dir 
= dirs
[0u]; 
 298             if ( !dir
.empty() && dir
[0u] == _T('~') ) 
 300                 curDir
.AssignDir(wxGetUserHome(dir
.c_str() + 1)); 
 309         wxArrayString dirsNew 
= curDir
.GetDirs(); 
 310         size_t count 
= dirs
.GetCount(); 
 311         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 313             dirsNew
.Add(dirs
[n
]); 
 319     // now deal with ".", ".." and the rest 
 321     size_t count 
= dirs
.GetCount(); 
 322     for ( size_t n 
= 0; n 
< count
; n
++ ) 
 324         wxString dir 
= dirs
[n
]; 
 326         if ( flags 
&& wxPATH_NORM_DOTS 
) 
 328             if ( dir 
== wxT(".") ) 
 334             if ( dir 
== wxT("..") ) 
 336                 if ( m_dirs
.IsEmpty() ) 
 338                     wxLogError(_("The path '%s' contains too many \"..\"!"), 
 339                                GetFullPath().c_str()); 
 343                 m_dirs
.Remove(m_dirs
.GetCount() - 1); 
 348         if ( flags 
& wxPATH_NORM_ENV_VARS 
) 
 350             dir 
= wxExpandEnvVars(dir
); 
 353         if ( (flags 
& wxPATH_NORM_CASE
) && !IsCaseSensitive(format
) ) 
 361     if ( (flags 
& wxPATH_NORM_CASE
) && !IsCaseSensitive(format
) ) 
 363         // VZ: expand env vars here too? 
 369 #if defined(__WXMSW__) && defined(__WIN32__) 
 370     if (flags 
& wxPATH_NORM_LONG
) 
 372         Assign(GetLongPath()); 
 379 // ---------------------------------------------------------------------------- 
 380 // filename kind tests 
 381 // ---------------------------------------------------------------------------- 
 383 bool wxFileName::SameAs( const wxFileName 
&filepath
, wxPathFormat format
) 
 385     wxFileName fn1 
= *this, 
 388     // get cwd only once - small time saving 
 389     wxString cwd 
= wxGetCwd(); 
 390     fn1
.Normalize(wxPATH_NORM_ALL
, cwd
, format
); 
 391     fn2
.Normalize(wxPATH_NORM_ALL
, cwd
, format
); 
 393     if ( fn1
.GetFullPath() == fn2
.GetFullPath() ) 
 396     // TODO: compare inodes for Unix, this works even when filenames are 
 397     //       different but files are the same (symlinks) (VZ) 
 403 bool wxFileName::IsCaseSensitive( wxPathFormat format 
) 
 405     // only DOS filenames are case-sensitive 
 406     return GetFormat(format
) != wxPATH_DOS
; 
 409 bool wxFileName::IsRelative( wxPathFormat format 
) 
 411     return !IsAbsolute(format
); 
 414 bool wxFileName::IsAbsolute( wxPathFormat format 
) 
 416     wxChar ch 
= m_dirs
.IsEmpty() ? _T('\0') : m_dirs
[0u][0u]; 
 418     // Hack to cope with e.g. c:\thing - need something better 
 419     wxChar driveSep 
= _T('\0'); 
 420     if (!m_dirs
.IsEmpty() && m_dirs
[0].Length() > 1) 
 421         driveSep 
= m_dirs
[0u][1u]; 
 423     // the path is absolute if it starts with a path separator or, only for 
 424     // Unix filenames, with "~" or "~user" 
 425     return IsPathSeparator(ch
, format
) || 
 426            driveSep 
== _T(':') || 
 427            (GetFormat(format
) == wxPATH_UNIX 
&& ch 
== _T('~') ); 
 431 wxString 
wxFileName::GetPathSeparators(wxPathFormat format
) 
 434     switch ( GetFormat(format
) ) 
 437             // accept both as native APIs do 
 438             seps 
<< wxFILE_SEP_PATH_UNIX 
<< wxFILE_SEP_PATH_DOS
; 
 442             wxFAIL_MSG( _T("unknown wxPATH_XXX style") ); 
 446             seps 
= wxFILE_SEP_PATH_UNIX
; 
 450             seps 
= wxFILE_SEP_PATH_MAC
; 
 458 bool wxFileName::IsPathSeparator(wxChar ch
, wxPathFormat format
) 
 460     return GetPathSeparators(format
).Find(ch
) != wxNOT_FOUND
; 
 463 bool wxFileName::IsWild( wxPathFormat format 
) 
 465     // FIXME: this is probably false for Mac and this is surely wrong for most 
 466     //        of Unix shells (think about "[...]") 
 468     return m_name
.find_first_of(_T("*?")) != wxString::npos
; 
 471 // ---------------------------------------------------------------------------- 
 472 // path components manipulation 
 473 // ---------------------------------------------------------------------------- 
 475 void wxFileName::AppendDir( const wxString 
&dir 
) 
 480 void wxFileName::PrependDir( const wxString 
&dir 
) 
 482     m_dirs
.Insert( dir
, 0 ); 
 485 void wxFileName::InsertDir( int before
, const wxString 
&dir 
) 
 487     m_dirs
.Insert( dir
, before 
); 
 490 void wxFileName::RemoveDir( int pos 
) 
 492     m_dirs
.Remove( (size_t)pos 
); 
 495 // ---------------------------------------------------------------------------- 
 497 // ---------------------------------------------------------------------------- 
 499 void wxFileName::SetFullName(const wxString
& fullname
) 
 501     SplitPath(fullname
, NULL 
/* no path */, &m_name
, &m_ext
); 
 504 wxString 
wxFileName::GetFullName() const 
 506     wxString fullname 
= m_name
; 
 507     if ( !m_ext
.empty() ) 
 509         fullname 
<< wxFILE_SEP_EXT 
<< m_ext
; 
 515 wxString 
wxFileName::GetPath( bool add_separator
, wxPathFormat format 
) const 
 517     format 
= GetFormat( format 
); 
 520     size_t count 
= m_dirs
.GetCount(); 
 521     for ( size_t i 
= 0; i 
< count
; i
++ ) 
 524         if ( add_separator 
|| (i 
< count
) ) 
 525             ret 
+= wxFILE_SEP_PATH
; 
 531 wxString 
wxFileName::GetFullPath( wxPathFormat format 
) const 
 533     format 
= GetFormat( format 
); 
 536     if (format 
== wxPATH_DOS
) 
 538         for (size_t i 
= 0; i 
< m_dirs
.GetCount(); i
++) 
 545     if (format 
== wxPATH_UNIX
) 
 547         for (size_t i 
= 0; i 
< m_dirs
.GetCount(); i
++) 
 555         for (size_t i 
= 0; i 
< m_dirs
.GetCount(); i
++) 
 564     if (!m_ext
.IsEmpty()) 
 573 // Return the short form of the path (returns identity on non-Windows platforms) 
 574 wxString 
wxFileName::GetShortPath() const 
 576 #if defined(__WXMSW__) && defined(__WIN32__) && !defined(__WXMICROWIN__) 
 577     wxString 
path(GetFullPath()); 
 579     DWORD sz 
= ::GetShortPathName(path
, NULL
, 0); 
 583         ok 
= ::GetShortPathName
 
 586                 pathOut
.GetWriteBuf(sz
), 
 589         pathOut
.UngetWriteBuf(); 
 596     return GetFullPath(); 
 600 // Return the long form of the path (returns identity on non-Windows platforms) 
 601 wxString 
wxFileName::GetLongPath() const 
 603 #if defined(__WXMSW__) && defined(__WIN32__) && !defined(__WXMICROWIN__) 
 604     wxString 
path(GetFullPath()); 
 606     bool success 
= FALSE
; 
 608 #if wxUSE_DYNLIB_CLASS 
 609     typedef DWORD (*GET_LONG_PATH_NAME
)(const wxChar 
*, wxChar 
*, DWORD
); 
 611     static bool s_triedToLoad 
= FALSE
; 
 613     if ( !s_triedToLoad 
) 
 615         s_triedToLoad 
= TRUE
; 
 617         wxDllType dllKernel 
= wxDllLoader::LoadLibrary(_T("kernel32")); 
 620             // may succeed or fail depending on the Windows version 
 621                         static GET_LONG_PATH_NAME s_pfnGetLongPathName 
= NULL
; 
 623             s_pfnGetLongPathName 
= (GET_LONG_PATH_NAME
) wxDllLoader::GetSymbol(dllKernel
, _T("GetLongPathNameW")); 
 625             s_pfnGetLongPathName 
= (GET_LONG_PATH_NAME
) wxDllLoader::GetSymbol(dllKernel
, _T("GetLongPathNameA")); 
 628             wxDllLoader::UnloadLibrary(dllKernel
); 
 630             if ( s_pfnGetLongPathName 
) 
 632                 DWORD dwSize 
= (*s_pfnGetLongPathName
)(path
, NULL
, 0); 
 633                 bool ok 
= dwSize 
> 0; 
 637                     DWORD sz 
= (*s_pfnGetLongPathName
)(path
, NULL
, 0); 
 641                         ok 
= (*s_pfnGetLongPathName
) 
 644                                 pathOut
.GetWriteBuf(sz
), 
 647                         pathOut
.UngetWriteBuf(); 
 659     // wxUSE_DYNLIB_CLASS 
 663         // The OS didn't support GetLongPathName, or some other error. 
 664         // We need to call FindFirstFile on each component in turn. 
 666         WIN32_FIND_DATA findFileData
; 
 668         pathOut 
= wxEmptyString
; 
 670         wxArrayString dirs 
= GetDirs(); 
 673         size_t count 
= dirs
.GetCount(); 
 677         for ( i 
= 0; i 
< count
; i
++ ) 
 679             // We're using pathOut to collect the long-name path, 
 680             // but using a temporary for appending the last path component which may be short-name 
 681             tmpPath 
= pathOut 
+ dirs
[i
]; 
 683             if (tmpPath
.Last() == wxT(':')) 
 685                 // Can't pass a drive and root dir to FindFirstFile, 
 686                 // so continue to next dir 
 687                 tmpPath 
+= wxFILE_SEP_PATH
; 
 692             hFind 
= ::FindFirstFile(tmpPath
, &findFileData
); 
 693             if (hFind 
== INVALID_HANDLE_VALUE
) 
 695                 // Error: return immediately with the original path 
 700                 pathOut 
+= findFileData
.cFileName
; 
 701                 if ( (i 
< (count
-1)) ) 
 702                     pathOut 
+= wxFILE_SEP_PATH
; 
 710     return GetFullPath(); 
 714 wxPathFormat 
wxFileName::GetFormat( wxPathFormat format 
) 
 716     if (format 
== wxPATH_NATIVE
) 
 718 #if defined(__WXMSW__) || defined(__WXPM__) 
 720 #elif defined(__WXMAC__) 
 721         format 
= wxPATH_UNIX
; // that's the way the rest of wx' code works right now 
 723         format 
= wxPATH_UNIX
; 
 729 // ---------------------------------------------------------------------------- 
 730 // path splitting function 
 731 // ---------------------------------------------------------------------------- 
 733 void wxFileName::SplitPath(const wxString
& fullpath
, 
 739     format 
= GetFormat(format
); 
 741     // find the positions of the last dot and last path separator in the path 
 742     size_t posLastDot 
= fullpath
.find_last_of(wxFILE_SEP_EXT
); 
 743     size_t posLastSlash 
= fullpath
.find_last_of(GetPathSeparators(format
)); 
 745     if ( (posLastDot 
!= wxString::npos
) && (format 
== wxPATH_UNIX
) ) 
 747         if ( (posLastDot 
== 0) || 
 748              (fullpath
[posLastDot 
- 1] == wxFILE_SEP_PATH_UNIX
) ) 
 750             // under Unix, dot may be (and commonly is) the first character of 
 751             // the filename, don't treat the entire filename as extension in 
 753             posLastDot 
= wxString::npos
; 
 757     // if we do have a dot and a slash, check that the dot is in the name part 
 758     if ( (posLastDot 
!= wxString::npos
) && 
 759          (posLastSlash 
!= wxString::npos
) && 
 760          (posLastDot 
< posLastSlash
) ) 
 762         // the dot is part of the path, not the start of the extension 
 763         posLastDot 
= wxString::npos
; 
 766     // now fill in the variables provided by user 
 769         if ( posLastSlash 
== wxString::npos 
) 
 776             // take all until the separator 
 777             *pstrPath 
= fullpath
.Left(posLastSlash
); 
 783         // take all characters starting from the one after the last slash and 
 784         // up to, but excluding, the last dot 
 785         size_t nStart 
= posLastSlash 
== wxString::npos 
? 0 : posLastSlash 
+ 1; 
 787         if ( posLastDot 
== wxString::npos 
) 
 789             // take all until the end 
 790             count 
= wxString::npos
; 
 792         else if ( posLastSlash 
== wxString::npos 
) 
 796         else // have both dot and slash 
 798             count 
= posLastDot 
- posLastSlash 
- 1; 
 801         *pstrName 
= fullpath
.Mid(nStart
, count
); 
 806         if ( posLastDot 
== wxString::npos 
) 
 813             // take everything after the dot 
 814             *pstrExt 
= fullpath
.Mid(posLastDot 
+ 1);