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
40 #include "wx/dynlib.h"
42 // For GetShort/LongPathName
45 #include "wx/msw/winundef.h"
48 // ============================================================================
50 // ============================================================================
52 // ----------------------------------------------------------------------------
53 // wxFileName construction
54 // ----------------------------------------------------------------------------
56 void wxFileName::Assign( const wxFileName
&filepath
)
58 m_ext
= filepath
.GetExt();
59 m_name
= filepath
.GetName();
60 m_dirs
= filepath
.GetDirs();
63 void wxFileName::Assign( const wxString
& path
,
68 wxStringTokenizer
tn(path
, GetPathSeparators(format
),
69 wxTOKEN_RET_EMPTY_ALL
);
72 while ( tn
.HasMoreTokens() )
74 wxString token
= tn
.GetNextToken();
76 // If the path starts with a slash, we need the first
77 // dir entry to be an empty for later reassembly.
78 if (first
|| !token
.IsEmpty())
88 void wxFileName::Assign(const wxString
& fullpath
,
91 wxString path
, name
, ext
;
92 SplitPath(fullpath
, &path
, &name
, &ext
, format
);
94 Assign(path
, name
, ext
, format
);
97 void wxFileName::Assign(const wxString
& path
,
98 const wxString
& fullname
,
102 SplitPath(fullname
, NULL
/* no path */, &name
, &ext
, format
);
104 Assign(path
, name
, ext
, format
);
107 void wxFileName::Clear()
111 m_ext
= wxEmptyString
;
115 wxFileName
wxFileName::FileName(const wxString
& file
)
117 return wxFileName(file
);
121 wxFileName
wxFileName::DirName(const wxString
& dir
)
128 // ----------------------------------------------------------------------------
130 // ----------------------------------------------------------------------------
132 bool wxFileName::FileExists()
134 return wxFileName::FileExists( GetFullPath() );
137 bool wxFileName::FileExists( const wxString
&file
)
139 return ::wxFileExists( file
);
142 bool wxFileName::DirExists()
144 return wxFileName::DirExists( GetFullPath() );
147 bool wxFileName::DirExists( const wxString
&dir
)
149 return ::wxDirExists( dir
);
152 // ----------------------------------------------------------------------------
153 // CWD and HOME stuff
154 // ----------------------------------------------------------------------------
156 void wxFileName::AssignCwd()
158 AssignDir(wxFileName::GetCwd());
162 wxString
wxFileName::GetCwd()
167 bool wxFileName::SetCwd()
169 return wxFileName::SetCwd( GetFullPath() );
172 bool wxFileName::SetCwd( const wxString
&cwd
)
174 return ::wxSetWorkingDirectory( cwd
);
177 void wxFileName::AssignHomeDir()
179 AssignDir(wxFileName::GetHomeDir());
182 wxString
wxFileName::GetHomeDir()
184 return ::wxGetHomeDir();
187 void wxFileName::AssignTempFileName( const wxString
&prefix
)
190 if ( wxGetTempFileName(prefix
, fullname
) )
200 // ----------------------------------------------------------------------------
201 // directory operations
202 // ----------------------------------------------------------------------------
204 bool wxFileName::Mkdir( int perm
)
206 return wxFileName::Mkdir( GetFullPath(), perm
);
209 bool wxFileName::Mkdir( const wxString
&dir
, int perm
)
211 return ::wxMkdir( dir
, perm
);
214 bool wxFileName::Rmdir()
216 return wxFileName::Rmdir( GetFullPath() );
219 bool wxFileName::Rmdir( const wxString
&dir
)
221 return ::wxRmdir( dir
);
224 // ----------------------------------------------------------------------------
225 // path normalization
226 // ----------------------------------------------------------------------------
228 bool wxFileName::Normalize(wxPathNormalize flags
,
232 // the existing path components
233 wxArrayString dirs
= GetDirs();
235 // the path to prepend in front to make the path absolute
238 format
= GetFormat(format
);
240 // make the path absolute
241 if ( (flags
& wxPATH_NORM_ABSOLUTE
) && !IsAbsolute() )
246 curDir
.AssignDir(cwd
);
249 // handle ~ stuff under Unix only
250 if ( (format
== wxPATH_UNIX
) && (flags
& wxPATH_NORM_TILDE
) )
252 if ( !dirs
.IsEmpty() )
254 wxString dir
= dirs
[0u];
255 if ( !dir
.empty() && dir
[0u] == _T('~') )
257 curDir
.AssignDir(wxGetUserHome(dir
.c_str() + 1));
266 wxArrayString dirsNew
= curDir
.GetDirs();
267 size_t count
= dirs
.GetCount();
268 for ( size_t n
= 0; n
< count
; n
++ )
270 dirsNew
.Add(dirs
[n
]);
276 // now deal with ".", ".." and the rest
278 size_t count
= dirs
.GetCount();
279 for ( size_t n
= 0; n
< count
; n
++ )
281 wxString dir
= dirs
[n
];
283 if ( flags
&& wxPATH_NORM_DOTS
)
285 if ( dir
== wxT(".") )
291 if ( dir
== wxT("..") )
293 if ( m_dirs
.IsEmpty() )
295 wxLogError(_("The path '%s' contains too many \"..\"!"),
296 GetFullPath().c_str());
300 m_dirs
.Remove(m_dirs
.GetCount() - 1);
305 if ( flags
& wxPATH_NORM_ENV_VARS
)
307 dir
= wxExpandEnvVars(dir
);
310 if ( (flags
& wxPATH_NORM_CASE
) && !IsCaseSensitive(format
) )
318 if ( (flags
& wxPATH_NORM_CASE
) && !IsCaseSensitive(format
) )
320 // VZ: expand env vars here too?
326 #if defined(__WXMSW__) && defined(__WIN32__)
327 if (flags
& wxPATH_NORM_LONG
)
329 Assign(GetLongPath());
336 // ----------------------------------------------------------------------------
337 // filename kind tests
338 // ----------------------------------------------------------------------------
340 bool wxFileName::SameAs( const wxFileName
&filepath
, wxPathFormat format
)
342 wxFileName fn1
= *this,
345 // get cwd only once - small time saving
346 wxString cwd
= wxGetCwd();
347 fn1
.Normalize(wxPATH_NORM_ALL
, cwd
, format
);
348 fn2
.Normalize(wxPATH_NORM_ALL
, cwd
, format
);
350 if ( fn1
.GetFullPath() == fn2
.GetFullPath() )
353 // TODO: compare inodes for Unix, this works even when filenames are
354 // different but files are the same (symlinks) (VZ)
360 bool wxFileName::IsCaseSensitive( wxPathFormat format
)
362 // only DOS filenames are case-sensitive
363 return GetFormat(format
) != wxPATH_DOS
;
366 bool wxFileName::IsRelative( wxPathFormat format
)
368 return !IsAbsolute(format
);
371 bool wxFileName::IsAbsolute( wxPathFormat format
)
373 wxChar ch
= m_dirs
.IsEmpty() ? _T('\0') : m_dirs
[0u][0u];
375 // Hack to cope with e.g. c:\thing - need something better
376 wxChar driveSep
= _T('\0');
377 if (!m_dirs
.IsEmpty() && m_dirs
[0].Length() > 1)
378 driveSep
= m_dirs
[0u][1u];
380 // the path is absolute if it starts with a path separator or, only for
381 // Unix filenames, with "~" or "~user"
382 return IsPathSeparator(ch
, format
) ||
383 driveSep
== _T(':') ||
384 (GetFormat(format
) == wxPATH_UNIX
&& ch
== _T('~') );
388 wxString
wxFileName::GetPathSeparators(wxPathFormat format
)
391 switch ( GetFormat(format
) )
394 // accept both as native APIs do
395 seps
<< wxFILE_SEP_PATH_UNIX
<< wxFILE_SEP_PATH_DOS
;
399 wxFAIL_MSG( _T("unknown wxPATH_XXX style") );
403 seps
= wxFILE_SEP_PATH_UNIX
;
407 seps
= wxFILE_SEP_PATH_MAC
;
415 bool wxFileName::IsPathSeparator(wxChar ch
, wxPathFormat format
)
417 return GetPathSeparators(format
).Find(ch
) != wxNOT_FOUND
;
420 bool wxFileName::IsWild( wxPathFormat format
)
422 // FIXME: this is probably false for Mac and this is surely wrong for most
423 // of Unix shells (think about "[...]")
424 return m_name
.find_first_of(_T("*?")) != wxString::npos
;
427 // ----------------------------------------------------------------------------
428 // path components manipulation
429 // ----------------------------------------------------------------------------
431 void wxFileName::AppendDir( const wxString
&dir
)
436 void wxFileName::PrependDir( const wxString
&dir
)
438 m_dirs
.Insert( dir
, 0 );
441 void wxFileName::InsertDir( int before
, const wxString
&dir
)
443 m_dirs
.Insert( dir
, before
);
446 void wxFileName::RemoveDir( int pos
)
448 m_dirs
.Remove( (size_t)pos
);
451 // ----------------------------------------------------------------------------
453 // ----------------------------------------------------------------------------
455 void wxFileName::SetFullName(const wxString
& fullname
)
457 SplitPath(fullname
, NULL
/* no path */, &m_name
, &m_ext
);
460 wxString
wxFileName::GetFullName() const
462 wxString fullname
= m_name
;
463 if ( !m_ext
.empty() )
465 fullname
<< wxFILE_SEP_EXT
<< m_ext
;
471 wxString
wxFileName::GetPath( bool add_separator
, wxPathFormat format
) const
473 format
= GetFormat( format
);
476 size_t count
= m_dirs
.GetCount();
477 for ( size_t i
= 0; i
< count
; i
++ )
480 if ( add_separator
|| (i
< count
) )
481 ret
+= wxFILE_SEP_PATH
;
487 wxString
wxFileName::GetFullPath( wxPathFormat format
) const
489 return GetPathWithSep() + GetFullName();
492 // Return the short form of the path (returns identity on non-Windows platforms)
493 wxString
wxFileName::GetShortPath() const
495 #if defined(__WXMSW__) && defined(__WIN32__)
496 wxString
path(GetFullPath());
498 DWORD sz
= ::GetShortPathName(path
, NULL
, 0);
502 ok
= ::GetShortPathName
505 pathOut
.GetWriteBuf(sz
),
508 pathOut
.UngetWriteBuf();
515 return GetFullPath();
519 // Return the long form of the path (returns identity on non-Windows platforms)
520 wxString
wxFileName::GetLongPath() const
522 #if defined(__WXMSW__) && defined(__WIN32__)
523 wxString
path(GetFullPath());
525 bool success
= FALSE
;
527 typedef DWORD (*GET_LONG_PATH_NAME
)(const wxChar
*, wxChar
*, DWORD
);
529 static bool s_triedToLoad
= FALSE
;
530 static GET_LONG_PATH_NAME s_pfnGetLongPathName
= NULL
;
532 if ( !s_triedToLoad
)
534 s_triedToLoad
= TRUE
;
536 wxDllType dllKernel
= wxDllLoader::LoadLibrary(_T("kernel32"));
539 // may succeed or fail depending on the Windows version
540 s_pfnGetLongPathName
= (GET_LONG_PATH_NAME
) wxDllLoader::GetSymbol(dllKernel
, _T("GetLongPathName"));
542 wxDllLoader::UnloadLibrary(dllKernel
);
544 if ( s_pfnGetLongPathName
)
546 DWORD dwSize
= (*s_pfnGetLongPathName
)(path
, NULL
, 0);
547 bool ok
= dwSize
> 0;
551 DWORD sz
= (*s_pfnGetLongPathName
)(path
, NULL
, 0);
555 ok
= (*s_pfnGetLongPathName
)
558 pathOut
.GetWriteBuf(sz
),
561 pathOut
.UngetWriteBuf();
574 // The OS didn't support GetLongPathName, or some other error.
575 // We need to call FindFirstFile on each component in turn.
577 WIN32_FIND_DATA findFileData
;
579 pathOut
= wxEmptyString
;
581 size_t count
= m_dirs
.GetCount();
584 for ( i
= 0; i
< count
; i
++ )
586 // We're using pathOut to collect the long-name path,
587 // but using a temporary for appending the last path component which may be short-name
588 tmpPath
= pathOut
+ m_dirs
[i
];
590 if (tmpPath
.Last() == wxT(':'))
591 tmpPath
+= wxFILE_SEP_PATH
;
593 hFind
= ::FindFirstFile(tmpPath
, &findFileData
);
594 if (hFind
== INVALID_HANDLE_VALUE
)
596 // Error: return immediately with the original path
601 pathOut
+= findFileData
.cFileName
;
603 pathOut
+= wxFILE_SEP_PATH
;
611 return GetFullPath();
615 wxPathFormat
wxFileName::GetFormat( wxPathFormat format
)
617 if (format
== wxPATH_NATIVE
)
619 #if defined(__WXMSW__) || defined(__WXPM__)
621 #elif defined(__WXMAC__)
622 format
= wxPATH_UNIX
; // that's the way the rest of wx' code works right now
624 format
= wxPATH_UNIX
;
630 // ----------------------------------------------------------------------------
631 // path splitting function
632 // ----------------------------------------------------------------------------
634 void wxFileName::SplitPath(const wxString
& fullpath
,
640 format
= GetFormat(format
);
642 // find the positions of the last dot and last path separator in the path
643 size_t posLastDot
= fullpath
.find_last_of(wxFILE_SEP_EXT
);
644 size_t posLastSlash
= fullpath
.find_last_of(GetPathSeparators(format
));
646 if ( (posLastDot
!= wxString::npos
) && (format
== wxPATH_UNIX
) )
648 if ( (posLastDot
== 0) ||
649 (fullpath
[posLastDot
- 1] == wxFILE_SEP_PATH_UNIX
) )
651 // under Unix, dot may be (and commonly is) the first character of
652 // the filename, don't treat the entire filename as extension in
654 posLastDot
= wxString::npos
;
658 // if we do have a dot and a slash, check that the dot is in the name part
659 if ( (posLastDot
!= wxString::npos
) &&
660 (posLastSlash
!= wxString::npos
) &&
661 (posLastDot
< posLastSlash
) )
663 // the dot is part of the path, not the start of the extension
664 posLastDot
= wxString::npos
;
667 // now fill in the variables provided by user
670 if ( posLastSlash
== wxString::npos
)
677 // take all until the separator
678 *pstrPath
= fullpath
.Left(posLastSlash
);
684 // take all characters starting from the one after the last slash and
685 // up to, but excluding, the last dot
686 size_t nStart
= posLastSlash
== wxString::npos
? 0 : posLastSlash
+ 1;
688 if ( posLastDot
== wxString::npos
)
690 // take all until the end
691 count
= wxString::npos
;
693 else if ( posLastSlash
== wxString::npos
)
697 else // have both dot and slash
699 count
= posLastDot
- posLastSlash
- 1;
702 *pstrName
= fullpath
.Mid(nStart
, count
);
707 if ( posLastDot
== wxString::npos
)
714 // take everything after the dot
715 *pstrExt
= fullpath
.Mid(posLastDot
+ 1);