Files
wxWidgets/src/common/filename.cpp
Ron Lee 4f89dbc48e removed dependency on windows.h from dynload.h
Moved dlopen implementation into wxDynamicLibrary.
Wrapped wxDllLoader in 2.2 compat guards and defined it in terms
of the new classes.
Added link option flags to wxDL and other missing functionality
previously implemented in wxDllLoader.
Modified wxDL++ classes to use wxDL base class implementation.
Replaced all uses of wxDllLoader in wx classes by the new
classes as appropriate.

def'd out (unimplemented) wxSystemSettingsBase so wxMSW would link.


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@13224 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2001-12-29 14:50:36 +00:00

1667 lines
45 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/common/filename.cpp
// Purpose: wxFileName - encapsulates a file path
// Author: Robert Roebling, Vadim Zeitlin
// Modified by:
// Created: 28.12.2000
// RCS-ID: $Id$
// Copyright: (c) 2000 Robert Roebling
// Licence: wxWindows license
/////////////////////////////////////////////////////////////////////////////
/*
Here are brief descriptions of the filename formats supported by this class:
wxPATH_UNIX: standard Unix format, used under Darwin as well, absolute file
names have the form:
/dir1/dir2/.../dirN/filename, "." and ".." stand for the
current and parent directory respectively, "~" is parsed as the
user HOME and "~username" as the HOME of that user
wxPATH_DOS: DOS/Windows format, absolute file names have the form:
drive:\dir1\dir2\...\dirN\filename.ext where drive is a single
letter. "." and ".." as for Unix but no "~".
There are also UNC names of the form \\share\fullpath
wxPATH_MAC: Mac OS 8/9 and Mac OS X under CodeWarrior 7 format, absolute file
names have the form
volume:dir1:...:dirN:filename
and the relative file names are either
:dir1:...:dirN:filename
or just
filename
(although :filename works as well).
Since the volume is just part of the file path, it is not
treated like a separate entity as it is done under DOS and
VMS, it is just treated as another dir.
wxPATH_VMS: VMS native format, absolute file names have the form
<device>:[dir1.dir2.dir3]file.txt
or
<device>:[000000.dir1.dir2.dir3]file.txt
the <device> is the physical device (i.e. disk). 000000 is the
root directory on the device which can be omitted.
Note that VMS uses different separators unlike Unix:
: always after the device. If the path does not contain : than
the default (the device of the current directory) is assumed.
[ start of directory specyfication
. separator between directory and subdirectory
] between directory and file
*/
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#ifdef __GNUG__
#pragma implementation "filename.h"
#endif
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include "wx/intl.h"
#include "wx/log.h"
#include "wx/file.h"
#endif
#include "wx/filename.h"
#include "wx/tokenzr.h"
#include "wx/config.h" // for wxExpandEnvVars
#include "wx/utils.h"
#include "wx/file.h"
//#include "wx/dynlib.h" // see GetLongPath below, code disabled.
// For GetShort/LongPathName
#ifdef __WIN32__
#include <windows.h>
#include "wx/msw/winundef.h"
#endif
// utime() is POSIX so should normally be available on all Unices
#ifdef __UNIX_LIKE__
#include <sys/types.h>
#include <utime.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
#ifdef __DJGPP__
#include <unistd.h>
#endif
#ifdef __MWERKS__
#include <stat.h>
#include <unistd.h>
#include <unix.h>
#endif
#ifdef __WATCOMC__
#include <io.h>
#include <sys/utime.h>
#include <sys/stat.h>
#endif
#ifdef __VISAGECPP__
#ifndef MAX_PATH
#define MAX_PATH 256
#endif
#endif
// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------
// small helper class which opens and closes the file - we use it just to get
// a file handle for the given file name to pass it to some Win32 API function
#if defined(__WIN32__) && !defined(__WXMICROWIN__)
class wxFileHandle
{
public:
wxFileHandle(const wxString& filename)
{
m_hFile = ::CreateFile
(
filename, // name
GENERIC_READ, // access mask
0, // no sharing
NULL, // no secutity attr
OPEN_EXISTING, // creation disposition
0, // no flags
NULL // no template file
);
if ( m_hFile == INVALID_HANDLE_VALUE )
{
wxLogSysError(_("Failed to open '%s' for reading"),
filename.c_str());
}
}
~wxFileHandle()
{
if ( m_hFile != INVALID_HANDLE_VALUE )
{
if ( !::CloseHandle(m_hFile) )
{
wxLogSysError(_("Failed to close file handle"));
}
}
}
// return TRUE only if the file could be opened successfully
bool IsOk() const { return m_hFile != INVALID_HANDLE_VALUE; }
// get the handle
operator HANDLE() const { return m_hFile; }
private:
HANDLE m_hFile;
};
#endif // __WIN32__
// ----------------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------------
#if defined(__WIN32__) && !defined(__WXMICROWIN__)
// convert between wxDateTime and FILETIME which is a 64-bit value representing
// the number of 100-nanosecond intervals since January 1, 1601.
// the number of milliseconds between the Unix Epoch (January 1, 1970) and the
// FILETIME reference point (January 1, 1601)
static const wxLongLong FILETIME_EPOCH_OFFSET = wxLongLong(0xa97, 0x30b66800);
static void ConvertFileTimeToWx(wxDateTime *dt, const FILETIME &ft)
{
wxLongLong ll(ft.dwHighDateTime, ft.dwLowDateTime);
// convert 100ns to ms
ll /= 10000;
// move it to our Epoch
ll -= FILETIME_EPOCH_OFFSET;
*dt = wxDateTime(ll);
}
static void ConvertWxToFileTime(FILETIME *ft, const wxDateTime& dt)
{
// do the reverse of ConvertFileTimeToWx()
wxLongLong ll = dt.GetValue();
ll *= 10000;
ll += FILETIME_EPOCH_OFFSET;
ft->dwHighDateTime = ll.GetHi();
ft->dwLowDateTime = ll.GetLo();
}
#endif // __WIN32__
// ============================================================================
// implementation
// ============================================================================
// ----------------------------------------------------------------------------
// wxFileName construction
// ----------------------------------------------------------------------------
void wxFileName::Assign( const wxFileName &filepath )
{
m_volume = filepath.GetVolume();
m_dirs = filepath.GetDirs();
m_name = filepath.GetName();
m_ext = filepath.GetExt();
m_relative = filepath.IsRelative();
}
void wxFileName::Assign(const wxString& volume,
const wxString& path,
const wxString& name,
const wxString& ext,
wxPathFormat format )
{
SetPath( path, format );
m_volume = volume;
m_ext = ext;
m_name = name;
}
void wxFileName::SetPath( const wxString &path, wxPathFormat format )
{
wxPathFormat my_format = GetFormat( format );
wxString my_path = path;
m_dirs.Clear();
if (!my_path.empty())
{
// 1) Determine if the path is relative or absolute.
switch (my_format)
{
case wxPATH_MAC:
m_relative = ( my_path[0u] == wxT(':') );
// We then remove a leading ":". The reason is in our
// storage form for relative paths:
// ":dir:file.txt" actually means "./dir/file.txt" in
// DOS notation and should get stored as
// (relative) (dir) (file.txt)
// "::dir:file.txt" actually means "../dir/file.txt"
// stored as (relative) (..) (dir) (file.txt)
// This is important only for the Mac as an empty dir
// actually means <UP>, whereas under DOS, double
// slashes can be ignored: "\\\\" is the same as "\\".
if (m_relative)
my_path.Remove( 0, 1 );
break;
case wxPATH_VMS:
// TODO: what is the relative path format here?
m_relative = FALSE;
break;
case wxPATH_UNIX:
m_relative = ( my_path[0u] != wxT('/') );
break;
case wxPATH_DOS:
m_relative = ( (my_path[0u] != wxT('/')) && (my_path[0u] != wxT('\\')) );
break;
default:
wxFAIL_MSG( wxT("error") );
break;
}
// 2) Break up the path into its members. If the original path
// was just "/" or "\\", m_dirs will be empty. We know from
// the m_relative field, if this means "nothing" or "root dir".
wxStringTokenizer tn( my_path, GetPathSeparators(my_format) );
while ( tn.HasMoreTokens() )
{
wxString token = tn.GetNextToken();
// Remove empty token under DOS and Unix, interpret them
// as .. under Mac.
if (token.empty())
{
if (my_format == wxPATH_MAC)
m_dirs.Add( wxT("..") );
// else ignore
}
else
{
m_dirs.Add( token );
}
}
}
else
{
m_relative = TRUE;
}
}
void wxFileName::Assign(const wxString& fullpath,
wxPathFormat format)
{
wxString volume, path, name, ext;
SplitPath(fullpath, &volume, &path, &name, &ext, format);
Assign(volume, path, name, ext, format);
}
void wxFileName::Assign(const wxString& fullpathOrig,
const wxString& fullname,
wxPathFormat format)
{
// always recognize fullpath as directory, even if it doesn't end with a
// slash
wxString fullpath = fullpathOrig;
if ( !wxEndsWithPathSeparator(fullpath) )
{
fullpath += GetPathSeparators(format)[0u];
}
wxString volume, path, name, ext;
// do some consistency checks in debug mode: the name should be really just
// the filename and the path should be really just a path
#ifdef __WXDEBUG__
wxString pathDummy, nameDummy, extDummy;
SplitPath(fullname, &pathDummy, &name, &ext, format);
wxASSERT_MSG( pathDummy.empty(),
_T("the file name shouldn't contain the path") );
SplitPath(fullpath, &volume, &path, &nameDummy, &extDummy, format);
wxASSERT_MSG( nameDummy.empty() && extDummy.empty(),
_T("the path shouldn't contain file name nor extension") );
#else // !__WXDEBUG__
SplitPath(fullname, NULL /* no path */, &name, &ext, format);
SplitPath(fullpath, &volume, &path, NULL, NULL, format);
#endif // __WXDEBUG__/!__WXDEBUG__
Assign(volume, path, name, ext, format);
}
void wxFileName::AssignDir(const wxString& dir, wxPathFormat format)
{
Assign(dir, _T(""), format);
}
void wxFileName::Clear()
{
m_dirs.Clear();
m_volume =
m_name =
m_ext = wxEmptyString;
}
/* static */
wxFileName wxFileName::FileName(const wxString& file)
{
return wxFileName(file);
}
/* static */
wxFileName wxFileName::DirName(const wxString& dir)
{
wxFileName fn;
fn.AssignDir(dir);
return fn;
}
// ----------------------------------------------------------------------------
// existence tests
// ----------------------------------------------------------------------------
bool wxFileName::FileExists()
{
return wxFileName::FileExists( GetFullPath() );
}
bool wxFileName::FileExists( const wxString &file )
{
return ::wxFileExists( file );
}
bool wxFileName::DirExists()
{
return wxFileName::DirExists( GetFullPath() );
}
bool wxFileName::DirExists( const wxString &dir )
{
return ::wxDirExists( dir );
}
// ----------------------------------------------------------------------------
// CWD and HOME stuff
// ----------------------------------------------------------------------------
void wxFileName::AssignCwd(const wxString& volume)
{
AssignDir(wxFileName::GetCwd(volume));
}
/* static */
wxString wxFileName::GetCwd(const wxString& volume)
{
// if we have the volume, we must get the current directory on this drive
// and to do this we have to chdir to this volume - at least under Windows,
// I don't know how to get the current drive on another volume elsewhere
// (TODO)
wxString cwdOld;
if ( !volume.empty() )
{
cwdOld = wxGetCwd();
SetCwd(volume + GetVolumeSeparator());
}
wxString cwd = ::wxGetCwd();
if ( !volume.empty() )
{
SetCwd(cwdOld);
}
return cwd;
}
bool wxFileName::SetCwd()
{
return wxFileName::SetCwd( GetFullPath() );
}
bool wxFileName::SetCwd( const wxString &cwd )
{
return ::wxSetWorkingDirectory( cwd );
}
void wxFileName::AssignHomeDir()
{
AssignDir(wxFileName::GetHomeDir());
}
wxString wxFileName::GetHomeDir()
{
return ::wxGetHomeDir();
}
void wxFileName::AssignTempFileName(const wxString& prefix, wxFile *fileTemp)
{
wxString tempname = CreateTempFileName(prefix, fileTemp);
if ( tempname.empty() )
{
// error, failed to get temp file name
Clear();
}
else // ok
{
Assign(tempname);
}
}
/* static */
wxString
wxFileName::CreateTempFileName(const wxString& prefix, wxFile *fileTemp)
{
wxString path, dir, name;
// use the directory specified by the prefix
SplitPath(prefix, &dir, &name, NULL /* extension */);
#if defined(__WINDOWS__) && !defined(__WXMICROWIN__)
#ifdef __WIN32__
if ( dir.empty() )
{
if ( !::GetTempPath(MAX_PATH, wxStringBuffer(dir, MAX_PATH + 1)) )
{
wxLogLastError(_T("GetTempPath"));
}
if ( dir.empty() )
{
// GetTempFileName() fails if we pass it an empty string
dir = _T('.');
}
}
if ( !::GetTempFileName(dir, name, 0, wxStringBuffer(path, MAX_PATH + 1)) )
{
wxLogLastError(_T("GetTempFileName"));
path.clear();
}
#else // Win16
if ( !::GetTempFileName(NULL, prefix, 0, wxStringBuffer(path, 1025)) )
{
path.clear();
}
#endif // Win32/16
#elif defined(__WXPM__)
// for now just create a file
//
// future enhancements can be to set some extended attributes for file
// systems OS/2 supports that have them (HPFS, FAT32) and security
// (HPFS386)
static const wxChar *szMktempSuffix = wxT("XXX");
path << dir << _T('/') << name << szMktempSuffix;
// Temporarily remove - MN
#ifndef __WATCOMC__
::DosCreateDir(wxStringBuffer(path, MAX_PATH), NULL);
#endif
#else // !Windows, !OS/2
if ( dir.empty() )
{
dir = wxGetenv(_T("TMP"));
if ( path.empty() )
{
dir = wxGetenv(_T("TEMP"));
}
if ( dir.empty() )
{
// default
#ifdef __DOS__
dir = _T(".");
#else
dir = _T("/tmp");
#endif
}
}
path = dir;
if ( !wxEndsWithPathSeparator(dir) &&
(name.empty() || !wxIsPathSeparator(name[0u])) )
{
path += wxFILE_SEP_PATH;
}
path += name;
#if defined(HAVE_MKSTEMP)
// scratch space for mkstemp()
path += _T("XXXXXX");
// can use the cast here because the length doesn't change and the string
// is not shared
int fdTemp = mkstemp((char *)path.mb_str());
if ( fdTemp == -1 )
{
// this might be not necessary as mkstemp() on most systems should have
// already done it but it doesn't hurt neither...
path.clear();
}
else // mkstemp() succeeded
{
// avoid leaking the fd
if ( fileTemp )
{
fileTemp->Attach(fdTemp);
}
else
{
close(fdTemp);
}
}
#else // !HAVE_MKSTEMP
#ifdef HAVE_MKTEMP
// same as above
path += _T("XXXXXX");
if ( !mktemp((char *)path.mb_str()) )
{
path.clear();
}
#else // !HAVE_MKTEMP (includes __DOS__)
// generate the unique file name ourselves
#ifndef __DOS__
path << (unsigned int)getpid();
#endif
wxString pathTry;
static const size_t numTries = 1000;
for ( size_t n = 0; n < numTries; n++ )
{
// 3 hex digits is enough for numTries == 1000 < 4096
pathTry = path + wxString::Format(_T("%.03x"), n);
if ( !wxFile::Exists(pathTry) )
{
break;
}
pathTry.clear();
}
path = pathTry;
#endif // HAVE_MKTEMP/!HAVE_MKTEMP
if ( !path.empty() )
{
}
#endif // HAVE_MKSTEMP/!HAVE_MKSTEMP
#endif // Windows/!Windows
if ( path.empty() )
{
wxLogSysError(_("Failed to create a temporary file name"));
}
else if ( fileTemp && !fileTemp->IsOpened() )
{
// open the file - of course, there is a race condition here, this is
// why we always prefer using mkstemp()...
//
// NB: GetTempFileName() under Windows creates the file, so using
// write_excl there would fail
if ( !fileTemp->Open(path,
#if defined(__WINDOWS__) && !defined(__WXMICROWIN__)
wxFile::write,
#else
wxFile::write_excl,
#endif
wxS_IRUSR | wxS_IWUSR) )
{
// FIXME: If !ok here should we loop and try again with another
// file name? That is the standard recourse if open(O_EXCL)
// fails, though of course it should be protected against
// possible infinite looping too.
wxLogError(_("Failed to open temporary file."));
path.clear();
}
}
return path;
}
// ----------------------------------------------------------------------------
// directory operations
// ----------------------------------------------------------------------------
bool wxFileName::Mkdir( int perm, bool full )
{
return wxFileName::Mkdir( GetFullPath(), perm, full );
}
bool wxFileName::Mkdir( const wxString &dir, int perm, bool full )
{
if (full)
{
wxFileName filename(dir);
wxArrayString dirs = filename.GetDirs();
dirs.Add(filename.GetName());
size_t count = dirs.GetCount();
size_t i;
wxString currPath;
int noErrors = 0;
for ( i = 0; i < count; i++ )
{
currPath += dirs[i];
if (currPath.Last() == wxT(':'))
{
// Can't create a root directory so continue to next dir
currPath += wxFILE_SEP_PATH;
continue;
}
if (!DirExists(currPath))
if (!wxMkdir(currPath, perm))
noErrors ++;
if ( (i < (count-1)) )
currPath += wxFILE_SEP_PATH;
}
return (noErrors == 0);
}
else
return ::wxMkdir( dir, perm );
}
bool wxFileName::Rmdir()
{
return wxFileName::Rmdir( GetFullPath() );
}
bool wxFileName::Rmdir( const wxString &dir )
{
return ::wxRmdir( dir );
}
// ----------------------------------------------------------------------------
// path normalization
// ----------------------------------------------------------------------------
bool wxFileName::Normalize(wxPathNormalize flags,
const wxString& cwd,
wxPathFormat format)
{
// the existing path components
wxArrayString dirs = GetDirs();
// the path to prepend in front to make the path absolute
wxFileName curDir;
format = GetFormat(format);
// make the path absolute
if ( (flags & wxPATH_NORM_ABSOLUTE) && m_relative )
{
if ( cwd.empty() )
{
curDir.AssignCwd(GetVolume());
}
else // cwd provided
{
curDir.AssignDir(cwd);
}
#if 0
// the path may be not absolute because it doesn't have the volume name
// but in this case we shouldn't modify the directory components of it
// but just set the current volume
if ( !HasVolume() && curDir.HasVolume() )
{
SetVolume(curDir.GetVolume());
if ( IsAbsolute() )
{
// yes, it was the case - we don't need curDir then
curDir.Clear();
}
}
#endif
m_relative = FALSE;
}
// handle ~ stuff under Unix only
if ( (format == wxPATH_UNIX) && (flags & wxPATH_NORM_TILDE) )
{
if ( !dirs.IsEmpty() )
{
wxString dir = dirs[0u];
if ( !dir.empty() && dir[0u] == _T('~') )
{
curDir.AssignDir(wxGetUserHome(dir.c_str() + 1));
dirs.RemoveAt(0u);
}
}
}
// transform relative path into abs one
if ( curDir.IsOk() )
{
wxArrayString dirsNew = curDir.GetDirs();
size_t count = dirs.GetCount();
for ( size_t n = 0; n < count; n++ )
{
dirsNew.Add(dirs[n]);
}
dirs = dirsNew;
}
// now deal with ".", ".." and the rest
m_dirs.Empty();
size_t count = dirs.GetCount();
for ( size_t n = 0; n < count; n++ )
{
wxString dir = dirs[n];
if ( flags & wxPATH_NORM_DOTS )
{
if ( dir == wxT(".") )
{
// just ignore
continue;
}
if ( dir == wxT("..") )
{
if ( m_dirs.IsEmpty() )
{
wxLogError(_("The path '%s' contains too many \"..\"!"),
GetFullPath().c_str());
return FALSE;
}
m_dirs.RemoveAt(m_dirs.GetCount() - 1);
continue;
}
}
if ( flags & wxPATH_NORM_ENV_VARS )
{
dir = wxExpandEnvVars(dir);
}
if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) )
{
dir.MakeLower();
}
m_dirs.Add(dir);
}
if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) )
{
// VZ: expand env vars here too?
m_name.MakeLower();
m_ext.MakeLower();
}
#if defined(__WIN32__)
if ( (flags & wxPATH_NORM_LONG) && (format == wxPATH_DOS) )
{
Assign(GetLongPath());
}
#endif // Win32
return TRUE;
}
bool wxFileName::MakeRelativeTo(const wxString& pathBase, wxPathFormat format)
{
wxFileName fnBase(pathBase, format);
// get cwd only once - small time saving
wxString cwd = wxGetCwd();
Normalize(wxPATH_NORM_ALL, cwd, format);
fnBase.Normalize(wxPATH_NORM_ALL, cwd, format);
bool withCase = IsCaseSensitive(format);
// we can't do anything if the files live on different volumes
if ( !GetVolume().IsSameAs(fnBase.GetVolume(), withCase) )
{
// nothing done
return FALSE;
}
// same drive, so we don't need our volume
m_volume.clear();
// remove common directories starting at the top
while ( !m_dirs.IsEmpty() && !fnBase.m_dirs.IsEmpty() &&
m_dirs[0u].IsSameAs(fnBase.m_dirs[0u], withCase) )
{
m_dirs.RemoveAt(0);
fnBase.m_dirs.RemoveAt(0);
}
// add as many ".." as needed
size_t count = fnBase.m_dirs.GetCount();
for ( size_t i = 0; i < count; i++ )
{
m_dirs.Insert(wxT(".."), 0u);
}
m_relative = TRUE;
// we were modified
return TRUE;
}
// ----------------------------------------------------------------------------
// filename kind tests
// ----------------------------------------------------------------------------
bool wxFileName::SameAs(const wxFileName &filepath, wxPathFormat format)
{
wxFileName fn1 = *this,
fn2 = filepath;
// get cwd only once - small time saving
wxString cwd = wxGetCwd();
fn1.Normalize(wxPATH_NORM_ALL, cwd, format);
fn2.Normalize(wxPATH_NORM_ALL, cwd, format);
if ( fn1.GetFullPath() == fn2.GetFullPath() )
return TRUE;
// TODO: compare inodes for Unix, this works even when filenames are
// different but files are the same (symlinks) (VZ)
return FALSE;
}
/* static */
bool wxFileName::IsCaseSensitive( wxPathFormat format )
{
// only Unix filenames are truely case-sensitive
return GetFormat(format) == wxPATH_UNIX;
}
/* static */
wxString wxFileName::GetVolumeSeparator(wxPathFormat format)
{
wxString sepVol;
if ( (GetFormat(format) == wxPATH_DOS) ||
(GetFormat(format) == wxPATH_VMS) )
{
sepVol = wxFILE_SEP_DSK;
}
//else: leave empty
return sepVol;
}
/* static */
wxString wxFileName::GetPathSeparators(wxPathFormat format)
{
wxString seps;
switch ( GetFormat(format) )
{
case wxPATH_DOS:
// accept both as native APIs do but put the native one first as
// this is the one we use in GetFullPath()
seps << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_UNIX;
break;
default:
wxFAIL_MSG( _T("unknown wxPATH_XXX style") );
// fall through
case wxPATH_UNIX:
seps = wxFILE_SEP_PATH_UNIX;
break;
case wxPATH_MAC:
seps = wxFILE_SEP_PATH_MAC;
break;
case wxPATH_VMS:
seps = wxFILE_SEP_PATH_VMS;
break;
}
return seps;
}
/* static */
bool wxFileName::IsPathSeparator(wxChar ch, wxPathFormat format)
{
// wxString::Find() doesn't work as expected with NUL - it will always find
// it, so it is almost surely a bug if this function is called with NUL arg
wxASSERT_MSG( ch != _T('\0'), _T("shouldn't be called with NUL") );
return GetPathSeparators(format).Find(ch) != wxNOT_FOUND;
}
bool wxFileName::IsWild( wxPathFormat format )
{
// FIXME: this is probably false for Mac and this is surely wrong for most
// of Unix shells (think about "[...]")
(void)format;
return m_name.find_first_of(_T("*?")) != wxString::npos;
}
// ----------------------------------------------------------------------------
// path components manipulation
// ----------------------------------------------------------------------------
void wxFileName::AppendDir( const wxString &dir )
{
m_dirs.Add( dir );
}
void wxFileName::PrependDir( const wxString &dir )
{
m_dirs.Insert( dir, 0 );
}
void wxFileName::InsertDir( int before, const wxString &dir )
{
m_dirs.Insert( dir, before );
}
void wxFileName::RemoveDir( int pos )
{
m_dirs.Remove( (size_t)pos );
}
// ----------------------------------------------------------------------------
// accessors
// ----------------------------------------------------------------------------
void wxFileName::SetFullName(const wxString& fullname)
{
SplitPath(fullname, NULL /* no path */, &m_name, &m_ext);
}
wxString wxFileName::GetFullName() const
{
wxString fullname = m_name;
if ( !m_ext.empty() )
{
fullname << wxFILE_SEP_EXT << m_ext;
}
return fullname;
}
wxString wxFileName::GetPath( bool add_separator, wxPathFormat format ) const
{
format = GetFormat( format );
wxString fullpath;
// the leading character
if ( format == wxPATH_MAC && m_relative )
{
fullpath += wxFILE_SEP_PATH_MAC;
}
else if ( format == wxPATH_DOS )
{
if (!m_relative)
fullpath += wxFILE_SEP_PATH_DOS;
}
else if ( format == wxPATH_UNIX )
{
if (!m_relative)
fullpath += wxFILE_SEP_PATH_UNIX;
}
// then concatenate all the path components using the path separator
size_t dirCount = m_dirs.GetCount();
if ( dirCount )
{
if ( format == wxPATH_VMS )
{
fullpath += wxT('[');
}
for ( size_t i = 0; i < dirCount; i++ )
{
// TODO: What to do with ".." under VMS
switch (format)
{
case wxPATH_MAC:
{
if (m_dirs[i] == wxT("."))
break;
if (m_dirs[i] != wxT("..")) // convert back from ".." to nothing
fullpath += m_dirs[i];
fullpath += wxT(':');
break;
}
case wxPATH_DOS:
{
fullpath += m_dirs[i];
fullpath += wxT('\\');
break;
}
case wxPATH_UNIX:
{
fullpath += m_dirs[i];
fullpath += wxT('/');
break;
}
case wxPATH_VMS:
{
if (m_dirs[i] != wxT("..")) // convert back from ".." to nothing
fullpath += m_dirs[i];
if (i == dirCount-1)
fullpath += wxT(']');
else
fullpath += wxT('.');
break;
}
default:
{
wxFAIL_MSG( wxT("error") );
}
}
}
}
return fullpath;
}
wxString wxFileName::GetFullPath( wxPathFormat format ) const
{
format = GetFormat(format);
wxString fullpath;
// first put the volume
if ( !m_volume.empty() )
{
{
// Special Windows UNC paths hack, part 2: undo what we did in
// SplitPath() and make an UNC path if we have a drive which is not a
// single letter (hopefully the network shares can't be one letter only
// although I didn't find any authoritative docs on this)
if ( format == wxPATH_DOS && m_volume.length() > 1 )
{
fullpath << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_DOS << m_volume;
}
else if ( format == wxPATH_DOS || format == wxPATH_VMS )
{
fullpath << m_volume << GetVolumeSeparator(format);
}
// else ignore
}
}
// the leading character
if ( format == wxPATH_MAC && m_relative )
{
fullpath += wxFILE_SEP_PATH_MAC;
}
else if ( format == wxPATH_DOS )
{
if (!m_relative)
fullpath += wxFILE_SEP_PATH_DOS;
}
else if ( format == wxPATH_UNIX )
{
if (!m_relative)
fullpath += wxFILE_SEP_PATH_UNIX;
}
// then concatenate all the path components using the path separator
size_t dirCount = m_dirs.GetCount();
if ( dirCount )
{
if ( format == wxPATH_VMS )
{
fullpath += wxT('[');
}
for ( size_t i = 0; i < dirCount; i++ )
{
// TODO: What to do with ".." under VMS
switch (format)
{
case wxPATH_MAC:
{
if (m_dirs[i] == wxT("."))
break;
if (m_dirs[i] != wxT("..")) // convert back from ".." to nothing
fullpath += m_dirs[i];
fullpath += wxT(':');
break;
}
case wxPATH_DOS:
{
fullpath += m_dirs[i];
fullpath += wxT('\\');
break;
}
case wxPATH_UNIX:
{
fullpath += m_dirs[i];
fullpath += wxT('/');
break;
}
case wxPATH_VMS:
{
if (m_dirs[i] != wxT("..")) // convert back from ".." to nothing
fullpath += m_dirs[i];
if (i == dirCount-1)
fullpath += wxT(']');
else
fullpath += wxT('.');
break;
}
default:
{
wxFAIL_MSG( wxT("error") );
}
}
}
}
// finally add the file name and extension
fullpath += GetFullName();
return fullpath;
}
// Return the short form of the path (returns identity on non-Windows platforms)
wxString wxFileName::GetShortPath() const
{
#if defined(__WXMSW__) && defined(__WIN32__) && !defined(__WXMICROWIN__)
wxString path(GetFullPath());
wxString pathOut;
DWORD sz = ::GetShortPathName(path, NULL, 0);
bool ok = sz != 0;
if ( ok )
{
ok = ::GetShortPathName
(
path,
pathOut.GetWriteBuf(sz),
sz
) != 0;
pathOut.UngetWriteBuf();
}
if (ok)
return pathOut;
return path;
#else
return GetFullPath();
#endif
}
// Return the long form of the path (returns identity on non-Windows platforms)
wxString wxFileName::GetLongPath() const
{
wxString pathOut,
path = GetFullPath();
#if defined(__WIN32__) && !defined(__WXMICROWIN__)
bool success = FALSE;
// VZ: this code was disabled, why?
#if 0 // wxUSE_DYNAMIC_LOADER
typedef DWORD (*GET_LONG_PATH_NAME)(const wxChar *, wxChar *, DWORD);
static bool s_triedToLoad = FALSE;
if ( !s_triedToLoad )
{
s_triedToLoad = TRUE;
wxDynamicLibrary dllKernel(_T("kernel32"));
if ( dllKernel.IsLoaded() )
{
// may succeed or fail depending on the Windows version
static GET_LONG_PATH_NAME s_pfnGetLongPathName = NULL;
#ifdef _UNICODE
s_pfnGetLongPathName = (GET_LONG_PATH_NAME) dllKernel.GetSymbol(_T("GetLongPathNameW"));
#else
s_pfnGetLongPathName = (GET_LONG_PATH_NAME) dllKernel.GetSymbol(_T("GetLongPathNameA"));
#endif
if ( s_pfnGetLongPathName )
{
DWORD dwSize = (*s_pfnGetLongPathName)(path, NULL, 0);
bool ok = dwSize > 0;
if ( ok )
{
DWORD sz = (*s_pfnGetLongPathName)(path, NULL, 0);
ok = sz != 0;
if ( ok )
{
ok = (*s_pfnGetLongPathName)
(
path,
pathOut.GetWriteBuf(sz),
sz
) != 0;
pathOut.UngetWriteBuf();
success = TRUE;
}
}
}
}
}
if (success)
return pathOut;
#endif // wxUSE_DYNAMIC_LOADER
if (!success)
{
// The OS didn't support GetLongPathName, or some other error.
// We need to call FindFirstFile on each component in turn.
WIN32_FIND_DATA findFileData;
HANDLE hFind;
pathOut = wxEmptyString;
wxArrayString dirs = GetDirs();
dirs.Add(GetFullName());
wxString tmpPath;
size_t count = dirs.GetCount();
for ( size_t i = 0; i < count; i++ )
{
// We're using pathOut to collect the long-name path, but using a
// temporary for appending the last path component which may be
// short-name
tmpPath = pathOut + dirs[i];
if ( tmpPath.empty() )
continue;
if ( tmpPath.Last() == wxT(':') )
{
// Can't pass a drive and root dir to FindFirstFile,
// so continue to next dir
tmpPath += wxFILE_SEP_PATH;
pathOut = tmpPath;
continue;
}
hFind = ::FindFirstFile(tmpPath, &findFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
// Error: return immediately with the original path
return path;
}
pathOut += findFileData.cFileName;
if ( (i < (count-1)) )
pathOut += wxFILE_SEP_PATH;
::FindClose(hFind);
}
}
#else // !Win32
pathOut = path;
#endif // Win32/!Win32
return pathOut;
}
wxPathFormat wxFileName::GetFormat( wxPathFormat format )
{
if (format == wxPATH_NATIVE)
{
#if defined(__WXMSW__) || defined(__WXPM__) || defined(__DOS__)
format = wxPATH_DOS;
#elif defined(__WXMAC__) && !defined(__DARWIN__)
format = wxPATH_MAC;
#elif defined(__VMS)
format = wxPATH_VMS;
#else
format = wxPATH_UNIX;
#endif
}
return format;
}
// ----------------------------------------------------------------------------
// path splitting function
// ----------------------------------------------------------------------------
/* static */
void wxFileName::SplitPath(const wxString& fullpathWithVolume,
wxString *pstrVolume,
wxString *pstrPath,
wxString *pstrName,
wxString *pstrExt,
wxPathFormat format)
{
format = GetFormat(format);
wxString fullpath = fullpathWithVolume;
// under VMS the end of the path is ']', not the path separator used to
// separate the components
wxString sepPath = format == wxPATH_VMS ? wxString(_T(']'))
: GetPathSeparators(format);
// special Windows UNC paths hack: transform \\share\path into share:path
if ( format == wxPATH_DOS )
{
if ( fullpath.length() >= 4 &&
fullpath[0u] == wxFILE_SEP_PATH_DOS &&
fullpath[1u] == wxFILE_SEP_PATH_DOS )
{
fullpath.erase(0, 2);
size_t posFirstSlash = fullpath.find_first_of(sepPath);
if ( posFirstSlash != wxString::npos )
{
fullpath[posFirstSlash] = wxFILE_SEP_DSK;
// UNC paths are always absolute, right? (FIXME)
fullpath.insert(posFirstSlash + 1, wxFILE_SEP_PATH_DOS);
}
}
}
// We separate the volume here
if ( format == wxPATH_DOS || format == wxPATH_VMS )
{
wxString sepVol = GetVolumeSeparator(format);
size_t posFirstColon = fullpath.find_first_of(sepVol);
if ( posFirstColon != wxString::npos )
{
if ( pstrVolume )
{
*pstrVolume = fullpath.Left(posFirstColon);
}
// remove the volume name and the separator from the full path
fullpath.erase(0, posFirstColon + sepVol.length());
}
}
// find the positions of the last dot and last path separator in the path
size_t posLastDot = fullpath.find_last_of(wxFILE_SEP_EXT);
size_t posLastSlash = fullpath.find_last_of(sepPath);
if ( (posLastDot != wxString::npos) &&
((format == wxPATH_UNIX) || (format == wxPATH_VMS)) )
{
if ( (posLastDot == 0) ||
(fullpath[posLastDot - 1] == sepPath[0u] ) )
{
// under Unix and VMS, dot may be (and commonly is) the first
// character of the filename, don't treat the entire filename as
// extension in this case
posLastDot = wxString::npos;
}
}
// if we do have a dot and a slash, check that the dot is in the name part
if ( (posLastDot != wxString::npos) &&
(posLastSlash != wxString::npos) &&
(posLastDot < posLastSlash) )
{
// the dot is part of the path, not the start of the extension
posLastDot = wxString::npos;
}
// now fill in the variables provided by user
if ( pstrPath )
{
if ( posLastSlash == wxString::npos )
{
// no path at all
pstrPath->Empty();
}
else
{
// take everything up to the path separator but take care to make
// the path equal to something like '/', not empty, for the files
// immediately under root directory
size_t len = posLastSlash;
if ( !len )
len++;
*pstrPath = fullpath.Left(len);
// special VMS hack: remove the initial bracket
if ( format == wxPATH_VMS )
{
if ( (*pstrPath)[0u] == _T('[') )
pstrPath->erase(0, 1);
}
}
}
if ( pstrName )
{
// take all characters starting from the one after the last slash and
// up to, but excluding, the last dot
size_t nStart = posLastSlash == wxString::npos ? 0 : posLastSlash + 1;
size_t count;
if ( posLastDot == wxString::npos )
{
// take all until the end
count = wxString::npos;
}
else if ( posLastSlash == wxString::npos )
{
count = posLastDot;
}
else // have both dot and slash
{
count = posLastDot - posLastSlash - 1;
}
*pstrName = fullpath.Mid(nStart, count);
}
if ( pstrExt )
{
if ( posLastDot == wxString::npos )
{
// no extension
pstrExt->Empty();
}
else
{
// take everything after the dot
*pstrExt = fullpath.Mid(posLastDot + 1);
}
}
}
/* static */
void wxFileName::SplitPath(const wxString& fullpath,
wxString *path,
wxString *name,
wxString *ext,
wxPathFormat format)
{
wxString volume;
SplitPath(fullpath, &volume, path, name, ext, format);
if ( path && !volume.empty() )
{
path->Prepend(volume + GetVolumeSeparator(format));
}
}
// ----------------------------------------------------------------------------
// time functions
// ----------------------------------------------------------------------------
bool wxFileName::SetTimes(const wxDateTime *dtCreate,
const wxDateTime *dtAccess,
const wxDateTime *dtMod)
{
#if defined(__UNIX_LIKE__) || (defined(__DOS__) && defined(__WATCOMC__))
if ( !dtAccess && !dtMod )
{
// can't modify the creation time anyhow, don't try
return TRUE;
}
// if dtAccess or dtMod is not specified, use the other one (which must be
// non NULL because of the test above) for both times
utimbuf utm;
utm.actime = dtAccess ? dtAccess->GetTicks() : dtMod->GetTicks();
utm.modtime = dtMod ? dtMod->GetTicks() : dtAccess->GetTicks();
if ( utime(GetFullPath(), &utm) == 0 )
{
return TRUE;
}
#elif defined(__WIN32__)
wxFileHandle fh(GetFullPath());
if ( fh.IsOk() )
{
FILETIME ftAccess, ftCreate, ftWrite;
if ( dtCreate )
ConvertWxToFileTime(&ftCreate, *dtCreate);
if ( dtAccess )
ConvertWxToFileTime(&ftAccess, *dtAccess);
if ( dtMod )
ConvertWxToFileTime(&ftWrite, *dtMod);
if ( ::SetFileTime(fh,
dtCreate ? &ftCreate : NULL,
dtAccess ? &ftAccess : NULL,
dtMod ? &ftWrite : NULL) )
{
return TRUE;
}
}
#else // other platform
#endif // platforms
wxLogSysError(_("Failed to modify file times for '%s'"),
GetFullPath().c_str());
return FALSE;
}
bool wxFileName::Touch()
{
#if defined(__UNIX_LIKE__)
// under Unix touching file is simple: just pass NULL to utime()
if ( utime(GetFullPath(), NULL) == 0 )
{
return TRUE;
}
wxLogSysError(_("Failed to touch the file '%s'"), GetFullPath().c_str());
return FALSE;
#else // other platform
wxDateTime dtNow = wxDateTime::Now();
return SetTimes(NULL /* don't change create time */, &dtNow, &dtNow);
#endif // platforms
}
bool wxFileName::GetTimes(wxDateTime *dtAccess,
wxDateTime *dtMod,
wxDateTime *dtChange) const
{
#if defined(__UNIX_LIKE__) || defined(__WXMAC__) || (defined(__DOS__) && defined(__WATCOMC__))
wxStructStat stBuf;
if ( wxStat(GetFullPath(), &stBuf) == 0 )
{
if ( dtAccess )
dtAccess->Set(stBuf.st_atime);
if ( dtMod )
dtMod->Set(stBuf.st_mtime);
if ( dtChange )
dtChange->Set(stBuf.st_ctime);
return TRUE;
}
#elif defined(__WIN32__)
wxFileHandle fh(GetFullPath());
if ( fh.IsOk() )
{
FILETIME ftAccess, ftCreate, ftWrite;
if ( ::GetFileTime(fh,
dtMod ? &ftCreate : NULL,
dtAccess ? &ftAccess : NULL,
dtChange ? &ftWrite : NULL) )
{
if ( dtMod )
ConvertFileTimeToWx(dtMod, ftCreate);
if ( dtAccess )
ConvertFileTimeToWx(dtAccess, ftAccess);
if ( dtChange )
ConvertFileTimeToWx(dtChange, ftWrite);
return TRUE;
}
}
#else // other platform
#endif // platforms
wxLogSysError(_("Failed to retrieve file times for '%s'"),
GetFullPath().c_str());
return FALSE;
}