wxFileName::MakeRelativeTo() is only supposed to change the path of the file, not its name, but it was doing the latter for the shortcuts as it implicitly resolved them to the name of their target. Fix this by ensuring we do not use wxPATH_NORM_SHORTCUT in MakeRelativeTo(). Closes #16239. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@77509 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2993 lines
84 KiB
C++
2993 lines
84 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/common/filename.cpp
|
|
// Purpose: wxFileName - encapsulates a file path
|
|
// Author: Robert Roebling, Vadim Zeitlin
|
|
// Modified by:
|
|
// Created: 28.12.2000
|
|
// Copyright: (c) 2000 Robert Roebling
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
/*
|
|
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 and
|
|
MSW unique volume names of the form \\?\Volume{GUID}\fullpath.
|
|
|
|
The latter provide a uniform way to access a volume regardless of
|
|
its current mount point, i.e. you can change a volume's mount
|
|
point from D: to E:, or even remove it, and still be able to
|
|
access it through its unique volume name. More on the subject can
|
|
be found in MSDN's article "Naming a Volume" that is currently at
|
|
http://msdn.microsoft.com/en-us/library/aa365248(VS.85).aspx.
|
|
|
|
|
|
wxPATH_MAC: Mac OS 8/9 only, not used any longer, 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 specification
|
|
. separator between directory and subdirectory
|
|
] between directory and file
|
|
*/
|
|
|
|
// ============================================================================
|
|
// declarations
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// For compilers that support precompilation, includes "wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#ifndef WX_PRECOMP
|
|
#ifdef __WINDOWS__
|
|
#include "wx/msw/wrapwin.h" // For GetShort/LongPathName
|
|
#endif
|
|
#include "wx/dynarray.h"
|
|
#include "wx/intl.h"
|
|
#include "wx/log.h"
|
|
#include "wx/utils.h"
|
|
#include "wx/crt.h"
|
|
#endif
|
|
|
|
#include "wx/filename.h"
|
|
#include "wx/private/filename.h"
|
|
#include "wx/tokenzr.h"
|
|
#include "wx/config.h" // for wxExpandEnvVars
|
|
#include "wx/dynlib.h"
|
|
#include "wx/dir.h"
|
|
#include "wx/longlong.h"
|
|
|
|
#if defined(__WIN32__) && defined(__MINGW32__)
|
|
#include "wx/msw/gccpriv.h"
|
|
#endif
|
|
|
|
#ifdef __WINDOWS__
|
|
#include "wx/msw/private.h"
|
|
#include <shlobj.h> // for CLSID_ShellLink
|
|
#include "wx/msw/missing.h"
|
|
#include "wx/msw/ole/oleutils.h"
|
|
#endif
|
|
|
|
#if defined(__WXMAC__)
|
|
#include "wx/osx/private.h" // includes mac headers
|
|
#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
|
|
|
|
#ifndef S_ISREG
|
|
#define S_ISREG(mode) ((mode) & S_IFREG)
|
|
#endif
|
|
#ifndef S_ISDIR
|
|
#define S_ISDIR(mode) ((mode) & S_IFDIR)
|
|
#endif
|
|
|
|
#if wxUSE_LONGLONG
|
|
extern const wxULongLong wxInvalidSize = (unsigned)-1;
|
|
#endif // wxUSE_LONGLONG
|
|
|
|
namespace
|
|
{
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// 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:
|
|
enum OpenMode
|
|
{
|
|
ReadAttr,
|
|
WriteAttr
|
|
};
|
|
|
|
wxFileHandle(const wxString& filename, OpenMode mode, int flags = 0)
|
|
{
|
|
// be careful and use FILE_{READ,WRITE}_ATTRIBUTES here instead of the
|
|
// usual GENERIC_{READ,WRITE} as we don't want the file access time to
|
|
// be changed when we open it because this class is used for setting
|
|
// access time (see #10567)
|
|
m_hFile = ::CreateFile
|
|
(
|
|
filename.t_str(), // name
|
|
mode == ReadAttr ? FILE_READ_ATTRIBUTES // access mask
|
|
: FILE_WRITE_ATTRIBUTES,
|
|
FILE_SHARE_READ | // sharing mode
|
|
FILE_SHARE_WRITE, // (allow everything)
|
|
NULL, // no secutity attr
|
|
OPEN_EXISTING, // creation disposition
|
|
flags, // flags
|
|
NULL // no template file
|
|
);
|
|
|
|
if ( m_hFile == INVALID_HANDLE_VALUE )
|
|
{
|
|
if ( mode == ReadAttr )
|
|
{
|
|
wxLogSysError(_("Failed to open '%s' for reading"),
|
|
filename.c_str());
|
|
}
|
|
else
|
|
{
|
|
wxLogSysError(_("Failed to open '%s' for writing"),
|
|
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 wxUSE_DATETIME && 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 UTC.
|
|
//
|
|
// This is the offset between FILETIME epoch and the Unix/wxDateTime Epoch.
|
|
static wxInt64 EPOCH_OFFSET_IN_MSEC = wxLL(11644473600000);
|
|
|
|
static void ConvertFileTimeToWx(wxDateTime *dt, const FILETIME &ft)
|
|
{
|
|
wxLongLong t(ft.dwHighDateTime, ft.dwLowDateTime);
|
|
t /= 10000; // Convert hundreds of nanoseconds to milliseconds.
|
|
t -= EPOCH_OFFSET_IN_MSEC;
|
|
|
|
*dt = wxDateTime(t);
|
|
}
|
|
|
|
static void ConvertWxToFileTime(FILETIME *ft, const wxDateTime& dt)
|
|
{
|
|
// Undo the conversions above.
|
|
wxLongLong t(dt.GetValue());
|
|
t += EPOCH_OFFSET_IN_MSEC;
|
|
t *= 10000;
|
|
|
|
ft->dwHighDateTime = t.GetHi();
|
|
ft->dwLowDateTime = t.GetLo();
|
|
}
|
|
|
|
#endif // wxUSE_DATETIME && __WIN32__
|
|
|
|
// return a string with the volume par
|
|
static wxString wxGetVolumeString(const wxString& volume, wxPathFormat format)
|
|
{
|
|
wxString path;
|
|
|
|
if ( !volume.empty() )
|
|
{
|
|
format = wxFileName::GetFormat(format);
|
|
|
|
// 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 && volume.length() > 1 )
|
|
{
|
|
// We also have to check for Windows unique volume names here and
|
|
// return it with '\\?\' prepended to it
|
|
if ( wxFileName::IsMSWUniqueVolumeNamePath("\\\\?\\" + volume + "\\",
|
|
format) )
|
|
{
|
|
path << "\\\\?\\" << volume;
|
|
}
|
|
else
|
|
{
|
|
// it must be a UNC path
|
|
path << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_DOS << volume;
|
|
}
|
|
}
|
|
else if ( format == wxPATH_DOS || format == wxPATH_VMS )
|
|
{
|
|
path << volume << wxFileName::GetVolumeSeparator(format);
|
|
}
|
|
// else ignore
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
// return true if the character is a DOS path separator i.e. either a slash or
|
|
// a backslash
|
|
inline bool IsDOSPathSep(wxUniChar ch)
|
|
{
|
|
return ch == wxFILE_SEP_PATH_DOS || ch == wxFILE_SEP_PATH_UNIX;
|
|
}
|
|
|
|
// return true if the format used is the DOS/Windows one and the string looks
|
|
// like a UNC path
|
|
static bool IsUNCPath(const wxString& path, wxPathFormat format)
|
|
{
|
|
return format == wxPATH_DOS &&
|
|
path.length() >= 4 && // "\\a" can't be a UNC path
|
|
IsDOSPathSep(path[0u]) &&
|
|
IsDOSPathSep(path[1u]) &&
|
|
!IsDOSPathSep(path[2u]);
|
|
}
|
|
|
|
// Under Unix-ish systems (basically everything except Windows but we can't
|
|
// just test for non-__WIN32__ because Cygwin defines it, yet we want to use
|
|
// lstat() under it, so test for all the rest explicitly) we may work either
|
|
// with the file itself or its target if it's a symbolic link and we should
|
|
// dereference it, as determined by wxFileName::ShouldFollowLink() and the
|
|
// absence of the wxFILE_EXISTS_NO_FOLLOW flag. StatAny() can be used to stat
|
|
// the appropriate file with an extra twist that it also works when there is no
|
|
// wxFileName object at all, as is the case in static methods.
|
|
|
|
#if defined(__UNIX_LIKE__) || defined(__WXMAC__)
|
|
#define wxHAVE_LSTAT
|
|
#endif
|
|
|
|
#ifdef wxHAVE_LSTAT
|
|
|
|
// Private implementation, don't call directly, use one of the overloads below.
|
|
bool DoStatAny(wxStructStat& st, wxString path, bool dereference)
|
|
{
|
|
// We need to remove any trailing slashes from the path because they could
|
|
// interfere with the symlink following decision: even if we use lstat(),
|
|
// it would still follow the symlink if we pass it a path with a slash at
|
|
// the end because the symlink resolution would happen while following the
|
|
// path and not for the last path element itself.
|
|
|
|
while ( wxEndsWithPathSeparator(path) )
|
|
{
|
|
const size_t posLast = path.length() - 1;
|
|
if ( !posLast )
|
|
{
|
|
// Don't turn "/" into empty string.
|
|
break;
|
|
}
|
|
|
|
path.erase(posLast);
|
|
}
|
|
|
|
int ret = dereference ? wxStat(path, &st) : wxLstat(path, &st);
|
|
return ret == 0;
|
|
}
|
|
|
|
// Overloads to use for a case when we don't have wxFileName object and when we
|
|
// do have one.
|
|
inline
|
|
bool StatAny(wxStructStat& st, const wxString& path, int flags)
|
|
{
|
|
return DoStatAny(st, path, !(flags & wxFILE_EXISTS_NO_FOLLOW));
|
|
}
|
|
|
|
inline
|
|
bool StatAny(wxStructStat& st, const wxFileName& fn)
|
|
{
|
|
return DoStatAny(st, fn.GetFullPath(), fn.ShouldFollowLink());
|
|
}
|
|
|
|
#endif // wxHAVE_LSTAT
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// private constants
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// length of \\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\ string
|
|
static const size_t wxMSWUniqueVolumePrefixLength = 49;
|
|
|
|
} // anonymous namespace
|
|
|
|
// ============================================================================
|
|
// 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.m_relative;
|
|
m_hasExt = filepath.m_hasExt;
|
|
m_dontFollowLinks = filepath.m_dontFollowLinks;
|
|
}
|
|
|
|
void wxFileName::Assign(const wxString& volume,
|
|
const wxString& path,
|
|
const wxString& name,
|
|
const wxString& ext,
|
|
bool hasExt,
|
|
wxPathFormat format)
|
|
{
|
|
// we should ignore paths which look like UNC shares because we already
|
|
// have the volume here and the UNC notation (\\server\path) is only valid
|
|
// for paths which don't start with a volume, so prevent SetPath() from
|
|
// recognizing "\\foo\bar" in "c:\\foo\bar" as an UNC path
|
|
//
|
|
// note also that this is a rather ugly way to do what we want (passing
|
|
// some kind of flag telling to ignore UNC paths to SetPath() would be
|
|
// better) but this is the safest thing to do to avoid breaking backwards
|
|
// compatibility in 2.8
|
|
if ( IsUNCPath(path, format) )
|
|
{
|
|
// remove one of the 2 leading backslashes to ensure that it's not
|
|
// recognized as an UNC path by SetPath()
|
|
wxString pathNonUNC(path, 1, wxString::npos);
|
|
SetPath(pathNonUNC, format);
|
|
}
|
|
else // no UNC complications
|
|
{
|
|
SetPath(path, format);
|
|
}
|
|
|
|
m_volume = volume;
|
|
m_ext = ext;
|
|
m_name = name;
|
|
|
|
m_hasExt = hasExt;
|
|
}
|
|
|
|
void wxFileName::SetPath( const wxString& pathOrig, wxPathFormat format )
|
|
{
|
|
m_dirs.Clear();
|
|
|
|
if ( pathOrig.empty() )
|
|
{
|
|
// no path at all
|
|
m_relative = true;
|
|
|
|
return;
|
|
}
|
|
|
|
format = GetFormat( format );
|
|
|
|
// 0) deal with possible volume part first
|
|
wxString volume,
|
|
path;
|
|
SplitVolume(pathOrig, &volume, &path, format);
|
|
if ( !volume.empty() )
|
|
{
|
|
m_relative = false;
|
|
|
|
SetVolume(volume);
|
|
}
|
|
|
|
// 1) Determine if the path is relative or absolute.
|
|
|
|
if ( path.empty() )
|
|
{
|
|
// we had only the volume
|
|
return;
|
|
}
|
|
|
|
wxChar leadingChar = path[0u];
|
|
|
|
switch (format)
|
|
{
|
|
case wxPATH_MAC:
|
|
m_relative = leadingChar == 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)
|
|
path.erase( 0, 1 );
|
|
break;
|
|
|
|
case wxPATH_VMS:
|
|
// TODO: what is the relative path format here?
|
|
m_relative = false;
|
|
break;
|
|
|
|
default:
|
|
wxFAIL_MSG( wxT("Unknown path format") );
|
|
wxFALLTHROUGH;
|
|
|
|
case wxPATH_UNIX:
|
|
m_relative = leadingChar != wxT('/');
|
|
break;
|
|
|
|
case wxPATH_DOS:
|
|
m_relative = !IsPathSeparator(leadingChar, format);
|
|
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( path, GetPathSeparators(format) );
|
|
|
|
while ( tn.HasMoreTokens() )
|
|
{
|
|
wxString token = tn.GetNextToken();
|
|
|
|
// Remove empty token under DOS and Unix, interpret them
|
|
// as .. under Mac.
|
|
if (token.empty())
|
|
{
|
|
if (format == wxPATH_MAC)
|
|
m_dirs.Add( wxT("..") );
|
|
// else ignore
|
|
}
|
|
else
|
|
{
|
|
m_dirs.Add( token );
|
|
}
|
|
}
|
|
}
|
|
|
|
void wxFileName::Assign(const wxString& fullpath,
|
|
wxPathFormat format)
|
|
{
|
|
wxString volume, path, name, ext;
|
|
bool hasExt;
|
|
SplitPath(fullpath, &volume, &path, &name, &ext, &hasExt, format);
|
|
|
|
Assign(volume, path, name, ext, hasExt, 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 ( !fullpath.empty() && !wxEndsWithPathSeparator(fullpath) )
|
|
{
|
|
fullpath += GetPathSeparator(format);
|
|
}
|
|
|
|
wxString volume, path, name, ext;
|
|
bool hasExt;
|
|
|
|
// do some consistency checks: the name should be really just the filename
|
|
// and the path should be really just a path
|
|
wxString volDummy, pathDummy, nameDummy, extDummy;
|
|
|
|
SplitPath(fullname, &volDummy, &pathDummy, &name, &ext, &hasExt, format);
|
|
|
|
wxASSERT_MSG( volDummy.empty() && pathDummy.empty(),
|
|
wxT("the file name shouldn't contain the path") );
|
|
|
|
SplitPath(fullpath, &volume, &path, &nameDummy, &extDummy, format);
|
|
|
|
#ifndef __VMS
|
|
// This test makes no sense on an OpenVMS system.
|
|
wxASSERT_MSG( nameDummy.empty() && extDummy.empty(),
|
|
wxT("the path shouldn't contain file name nor extension") );
|
|
#endif
|
|
Assign(volume, path, name, ext, hasExt, format);
|
|
}
|
|
|
|
void wxFileName::Assign(const wxString& pathOrig,
|
|
const wxString& name,
|
|
const wxString& ext,
|
|
wxPathFormat format)
|
|
{
|
|
wxString volume,
|
|
path;
|
|
SplitVolume(pathOrig, &volume, &path, format);
|
|
|
|
Assign(volume, path, name, ext, format);
|
|
}
|
|
|
|
void wxFileName::AssignDir(const wxString& dir, wxPathFormat format)
|
|
{
|
|
Assign(dir, wxEmptyString, format);
|
|
}
|
|
|
|
void wxFileName::Clear()
|
|
{
|
|
m_dirs.clear();
|
|
m_volume.clear();
|
|
m_name.clear();
|
|
m_ext.clear();
|
|
|
|
// we don't have any absolute path for now
|
|
m_relative = true;
|
|
|
|
// nor any extension
|
|
m_hasExt = false;
|
|
|
|
// follow symlinks by default
|
|
m_dontFollowLinks = false;
|
|
}
|
|
|
|
/* static */
|
|
wxFileName wxFileName::FileName(const wxString& file, wxPathFormat format)
|
|
{
|
|
return wxFileName(file, format);
|
|
}
|
|
|
|
/* static */
|
|
wxFileName wxFileName::DirName(const wxString& dir, wxPathFormat format)
|
|
{
|
|
wxFileName fn;
|
|
fn.AssignDir(dir, format);
|
|
return fn;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// existence tests
|
|
// ----------------------------------------------------------------------------
|
|
|
|
namespace
|
|
{
|
|
|
|
#if defined(__WINDOWS__) && !defined(__WXMICROWIN__)
|
|
|
|
void RemoveTrailingSeparatorsFromPath(wxString& strPath)
|
|
{
|
|
// Windows fails to find directory named "c:\dir\" even if "c:\dir" exists,
|
|
// so remove all trailing backslashes from the path - but don't do this for
|
|
// the paths "d:\" (which are different from "d:"), for just "\" or for
|
|
// windows unique volume names ("\\?\Volume{GUID}\")
|
|
while ( wxEndsWithPathSeparator( strPath ) )
|
|
{
|
|
size_t len = strPath.length();
|
|
if ( len == 1 || (len == 3 && strPath[len - 2] == wxT(':')) ||
|
|
(len == wxMSWUniqueVolumePrefixLength &&
|
|
wxFileName::IsMSWUniqueVolumeNamePath(strPath)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
strPath.Truncate(len - 1);
|
|
}
|
|
}
|
|
|
|
#endif // __WINDOWS_
|
|
|
|
bool
|
|
wxFileSystemObjectExists(const wxString& path, int flags)
|
|
{
|
|
|
|
// Should the existence of file/directory with this name be accepted, i.e.
|
|
// result in the true return value from this function?
|
|
const bool acceptFile = (flags & wxFILE_EXISTS_REGULAR) != 0;
|
|
const bool acceptDir = (flags & wxFILE_EXISTS_DIR) != 0;
|
|
|
|
wxString strPath(path);
|
|
|
|
#if defined(__WINDOWS__) && !defined(__WXMICROWIN__)
|
|
if ( acceptDir )
|
|
{
|
|
// Ensure that the path doesn't have any trailing separators when
|
|
// checking for directories.
|
|
RemoveTrailingSeparatorsFromPath(strPath);
|
|
}
|
|
|
|
// we must use GetFileAttributes() instead of the ANSI C functions because
|
|
// it can cope with network (UNC) paths unlike them
|
|
DWORD ret = ::GetFileAttributes(strPath.t_str());
|
|
|
|
if ( ret == INVALID_FILE_ATTRIBUTES )
|
|
return false;
|
|
|
|
if ( ret & FILE_ATTRIBUTE_DIRECTORY )
|
|
return acceptDir;
|
|
|
|
// Anything else must be a file (perhaps we should check for
|
|
// FILE_ATTRIBUTE_REPARSE_POINT?)
|
|
return acceptFile;
|
|
#else // Non-MSW, non-OS/2
|
|
wxStructStat st;
|
|
if ( !StatAny(st, strPath, flags) )
|
|
return false;
|
|
|
|
if ( S_ISREG(st.st_mode) )
|
|
return acceptFile;
|
|
if ( S_ISDIR(st.st_mode) )
|
|
return acceptDir;
|
|
if ( S_ISLNK(st.st_mode) )
|
|
{
|
|
// Take care to not test for "!= 0" here as this would erroneously
|
|
// return true if only wxFILE_EXISTS_NO_FOLLOW, which is part of
|
|
// wxFILE_EXISTS_SYMLINK, is set too.
|
|
return (flags & wxFILE_EXISTS_SYMLINK) == wxFILE_EXISTS_SYMLINK;
|
|
}
|
|
if ( S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) )
|
|
return (flags & wxFILE_EXISTS_DEVICE) != 0;
|
|
if ( S_ISFIFO(st.st_mode) )
|
|
return (flags & wxFILE_EXISTS_FIFO) != 0;
|
|
if ( S_ISSOCK(st.st_mode) )
|
|
return (flags & wxFILE_EXISTS_SOCKET) != 0;
|
|
|
|
return flags & wxFILE_EXISTS_ANY;
|
|
#endif // Platforms
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
bool wxFileName::FileExists() const
|
|
{
|
|
int flags = wxFILE_EXISTS_REGULAR;
|
|
if ( !ShouldFollowLink() )
|
|
flags |= wxFILE_EXISTS_NO_FOLLOW;
|
|
|
|
return wxFileSystemObjectExists(GetFullPath(), flags);
|
|
}
|
|
|
|
/* static */
|
|
bool wxFileName::FileExists( const wxString &filePath )
|
|
{
|
|
return wxFileSystemObjectExists(filePath, wxFILE_EXISTS_REGULAR);
|
|
}
|
|
|
|
bool wxFileName::DirExists() const
|
|
{
|
|
int flags = wxFILE_EXISTS_DIR;
|
|
if ( !ShouldFollowLink() )
|
|
flags |= wxFILE_EXISTS_NO_FOLLOW;
|
|
|
|
return Exists(GetPath(), flags);
|
|
}
|
|
|
|
/* static */
|
|
bool wxFileName::DirExists( const wxString &dirPath )
|
|
{
|
|
return wxFileSystemObjectExists(dirPath, wxFILE_EXISTS_DIR);
|
|
}
|
|
|
|
bool wxFileName::Exists(int flags) const
|
|
{
|
|
// Notice that wxFILE_EXISTS_NO_FOLLOW may be specified in the flags even
|
|
// if our DontFollowLink() hadn't been called and we do honour it then. But
|
|
// if the user took the care of calling DontFollowLink(), it is always
|
|
// taken into account.
|
|
if ( !ShouldFollowLink() )
|
|
flags |= wxFILE_EXISTS_NO_FOLLOW;
|
|
|
|
return wxFileSystemObjectExists(GetFullPath(), flags);
|
|
}
|
|
|
|
/* static */
|
|
bool wxFileName::Exists(const wxString& path, int flags)
|
|
{
|
|
return wxFileSystemObjectExists(path, flags);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// 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() const
|
|
{
|
|
return wxFileName::SetCwd( GetPath() );
|
|
}
|
|
|
|
bool wxFileName::SetCwd( const wxString &cwd )
|
|
{
|
|
return ::wxSetWorkingDirectory( cwd );
|
|
}
|
|
|
|
void wxFileName::AssignHomeDir()
|
|
{
|
|
AssignDir(wxFileName::GetHomeDir());
|
|
}
|
|
|
|
wxString wxFileName::GetHomeDir()
|
|
{
|
|
return ::wxGetHomeDir();
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// CreateTempFileName
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if wxUSE_FILE || wxUSE_FFILE
|
|
|
|
|
|
#if !defined wx_fdopen && defined HAVE_FDOPEN
|
|
#define wx_fdopen fdopen
|
|
#endif
|
|
|
|
// NB: GetTempFileName() under Windows creates the file, so using
|
|
// O_EXCL there would fail
|
|
#ifdef __WINDOWS__
|
|
#define wxOPEN_EXCL 0
|
|
#else
|
|
#define wxOPEN_EXCL O_EXCL
|
|
#endif
|
|
|
|
|
|
#ifdef wxOpenOSFHandle
|
|
#define WX_HAVE_DELETE_ON_CLOSE
|
|
// On Windows create a file with the FILE_FLAGS_DELETE_ON_CLOSE flags.
|
|
//
|
|
static int wxOpenWithDeleteOnClose(const wxString& filename)
|
|
{
|
|
DWORD access = GENERIC_READ | GENERIC_WRITE;
|
|
|
|
DWORD disposition = OPEN_ALWAYS;
|
|
|
|
DWORD attributes = FILE_ATTRIBUTE_TEMPORARY |
|
|
FILE_FLAG_DELETE_ON_CLOSE;
|
|
|
|
HANDLE h = ::CreateFile(filename.t_str(), access, 0, NULL,
|
|
disposition, attributes, NULL);
|
|
|
|
return wxOpenOSFHandle(h, wxO_BINARY);
|
|
}
|
|
#endif // wxOpenOSFHandle
|
|
|
|
|
|
// Helper to open the file
|
|
//
|
|
static int wxTempOpen(const wxString& path, bool *deleteOnClose)
|
|
{
|
|
#ifdef WX_HAVE_DELETE_ON_CLOSE
|
|
if (*deleteOnClose)
|
|
return wxOpenWithDeleteOnClose(path);
|
|
#endif
|
|
|
|
*deleteOnClose = false;
|
|
|
|
return wxOpen(path, wxO_BINARY | O_RDWR | O_CREAT | wxOPEN_EXCL, 0600);
|
|
}
|
|
|
|
|
|
#if wxUSE_FFILE
|
|
// Helper to open the file and attach it to the wxFFile
|
|
//
|
|
static bool wxTempOpen(wxFFile *file, const wxString& path, bool *deleteOnClose)
|
|
{
|
|
#ifndef wx_fdopen
|
|
*deleteOnClose = false;
|
|
return file->Open(path, wxT("w+b"));
|
|
#else // wx_fdopen
|
|
int fd = wxTempOpen(path, deleteOnClose);
|
|
if (fd == -1)
|
|
return false;
|
|
file->Attach(wx_fdopen(fd, "w+b"), path);
|
|
return file->IsOpened();
|
|
#endif // wx_fdopen
|
|
}
|
|
#endif // wxUSE_FFILE
|
|
|
|
|
|
#if !wxUSE_FILE
|
|
#define WXFILEARGS(x, y) y
|
|
#elif !wxUSE_FFILE
|
|
#define WXFILEARGS(x, y) x
|
|
#else
|
|
#define WXFILEARGS(x, y) x, y
|
|
#endif
|
|
|
|
|
|
// Implementation of wxFileName::CreateTempFileName().
|
|
//
|
|
static wxString wxCreateTempImpl(
|
|
const wxString& prefix,
|
|
WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp),
|
|
bool *deleteOnClose = NULL)
|
|
{
|
|
#if wxUSE_FILE && wxUSE_FFILE
|
|
wxASSERT(fileTemp == NULL || ffileTemp == NULL);
|
|
#endif
|
|
wxString path, dir, name;
|
|
bool wantDeleteOnClose = false;
|
|
|
|
if (deleteOnClose)
|
|
{
|
|
// set the result to false initially
|
|
wantDeleteOnClose = *deleteOnClose;
|
|
*deleteOnClose = false;
|
|
}
|
|
else
|
|
{
|
|
// easier if it alwasys points to something
|
|
deleteOnClose = &wantDeleteOnClose;
|
|
}
|
|
|
|
// use the directory specified by the prefix
|
|
wxFileName::SplitPath(prefix, &dir, &name, NULL /* extension */);
|
|
|
|
if (dir.empty())
|
|
{
|
|
dir = wxFileName::GetTempDir();
|
|
}
|
|
|
|
#if defined(__WXWINCE__)
|
|
path = dir + wxT("\\") + name;
|
|
int i = 1;
|
|
while (wxFileName::FileExists(path))
|
|
{
|
|
path = dir + wxT("\\") + name ;
|
|
path << i;
|
|
i ++;
|
|
}
|
|
|
|
#elif defined(__WINDOWS__) && !defined(__WXMICROWIN__)
|
|
if (!::GetTempFileName(dir.t_str(), name.t_str(), 0,
|
|
wxStringBuffer(path, MAX_PATH + 1)))
|
|
{
|
|
wxLogLastError(wxT("GetTempFileName"));
|
|
|
|
path.clear();
|
|
}
|
|
|
|
#else // !Windows
|
|
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 += wxT("XXXXXX");
|
|
|
|
// we need to copy the path to the buffer in which mkstemp() can modify it
|
|
wxCharBuffer buf(path.fn_str());
|
|
|
|
// cast is safe because the string length doesn't change
|
|
int fdTemp = mkstemp( (char*)(const char*) buf );
|
|
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
|
|
{
|
|
path = wxConvFile.cMB2WX( (const char*) buf );
|
|
|
|
#if wxUSE_FILE
|
|
// avoid leaking the fd
|
|
if ( fileTemp )
|
|
{
|
|
fileTemp->Attach(fdTemp);
|
|
}
|
|
else
|
|
#endif
|
|
|
|
#if wxUSE_FFILE
|
|
if ( ffileTemp )
|
|
{
|
|
#ifdef wx_fdopen
|
|
ffileTemp->Attach(wx_fdopen(fdTemp, "r+b"), path);
|
|
#else
|
|
ffileTemp->Open(path, wxT("r+b"));
|
|
close(fdTemp);
|
|
#endif
|
|
}
|
|
else
|
|
#endif
|
|
|
|
{
|
|
close(fdTemp);
|
|
}
|
|
}
|
|
#else // !HAVE_MKSTEMP
|
|
|
|
#ifdef HAVE_MKTEMP
|
|
// same as above
|
|
path += wxT("XXXXXX");
|
|
|
|
wxCharBuffer buf = wxConvFile.cWX2MB( path );
|
|
if ( !mktemp( (char*)(const char*) buf ) )
|
|
{
|
|
path.clear();
|
|
}
|
|
else
|
|
{
|
|
path = wxConvFile.cMB2WX( (const char*) buf );
|
|
}
|
|
#else // !HAVE_MKTEMP (includes __DOS__)
|
|
// generate the unique file name ourselves
|
|
#if !defined(__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(wxT("%.03x"), (unsigned int) n);
|
|
if ( !wxFileName::FileExists(pathTry) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
pathTry.clear();
|
|
}
|
|
|
|
path = pathTry;
|
|
#endif // HAVE_MKTEMP/!HAVE_MKTEMP
|
|
|
|
#endif // HAVE_MKSTEMP/!HAVE_MKSTEMP
|
|
|
|
#endif // Windows/!Windows
|
|
|
|
if ( path.empty() )
|
|
{
|
|
wxLogSysError(_("Failed to create a temporary file name"));
|
|
}
|
|
else
|
|
{
|
|
bool ok = true;
|
|
|
|
// open the file - of course, there is a race condition here, this is
|
|
// why we always prefer using mkstemp()...
|
|
#if wxUSE_FILE
|
|
if ( fileTemp && !fileTemp->IsOpened() )
|
|
{
|
|
*deleteOnClose = wantDeleteOnClose;
|
|
int fd = wxTempOpen(path, deleteOnClose);
|
|
if (fd != -1)
|
|
fileTemp->Attach(fd);
|
|
else
|
|
ok = false;
|
|
}
|
|
#endif
|
|
|
|
#if wxUSE_FFILE
|
|
if ( ffileTemp && !ffileTemp->IsOpened() )
|
|
{
|
|
*deleteOnClose = wantDeleteOnClose;
|
|
ok = wxTempOpen(ffileTemp, path, deleteOnClose);
|
|
}
|
|
#endif
|
|
|
|
if ( !ok )
|
|
{
|
|
// 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;
|
|
}
|
|
|
|
|
|
static bool wxCreateTempImpl(
|
|
const wxString& prefix,
|
|
WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp),
|
|
wxString *name)
|
|
{
|
|
bool deleteOnClose = true;
|
|
|
|
*name = wxCreateTempImpl(prefix,
|
|
WXFILEARGS(fileTemp, ffileTemp),
|
|
&deleteOnClose);
|
|
|
|
bool ok = !name->empty();
|
|
|
|
if (deleteOnClose)
|
|
name->clear();
|
|
#ifdef __UNIX__
|
|
else if (ok && wxRemoveFile(*name))
|
|
name->clear();
|
|
#endif
|
|
|
|
return ok;
|
|
}
|
|
|
|
|
|
static void wxAssignTempImpl(
|
|
wxFileName *fn,
|
|
const wxString& prefix,
|
|
WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp))
|
|
{
|
|
wxString tempname;
|
|
tempname = wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, ffileTemp));
|
|
|
|
if ( tempname.empty() )
|
|
{
|
|
// error, failed to get temp file name
|
|
fn->Clear();
|
|
}
|
|
else // ok
|
|
{
|
|
fn->Assign(tempname);
|
|
}
|
|
}
|
|
|
|
|
|
void wxFileName::AssignTempFileName(const wxString& prefix)
|
|
{
|
|
wxAssignTempImpl(this, prefix, WXFILEARGS(NULL, NULL));
|
|
}
|
|
|
|
/* static */
|
|
wxString wxFileName::CreateTempFileName(const wxString& prefix)
|
|
{
|
|
return wxCreateTempImpl(prefix, WXFILEARGS(NULL, NULL));
|
|
}
|
|
|
|
#endif // wxUSE_FILE || wxUSE_FFILE
|
|
|
|
|
|
#if wxUSE_FILE
|
|
|
|
wxString wxCreateTempFileName(const wxString& prefix,
|
|
wxFile *fileTemp,
|
|
bool *deleteOnClose)
|
|
{
|
|
return wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, NULL), deleteOnClose);
|
|
}
|
|
|
|
bool wxCreateTempFile(const wxString& prefix,
|
|
wxFile *fileTemp,
|
|
wxString *name)
|
|
{
|
|
return wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, NULL), name);
|
|
}
|
|
|
|
void wxFileName::AssignTempFileName(const wxString& prefix, wxFile *fileTemp)
|
|
{
|
|
wxAssignTempImpl(this, prefix, WXFILEARGS(fileTemp, NULL));
|
|
}
|
|
|
|
/* static */
|
|
wxString
|
|
wxFileName::CreateTempFileName(const wxString& prefix, wxFile *fileTemp)
|
|
{
|
|
return wxCreateTempFileName(prefix, fileTemp);
|
|
}
|
|
|
|
#endif // wxUSE_FILE
|
|
|
|
|
|
#if wxUSE_FFILE
|
|
|
|
wxString wxCreateTempFileName(const wxString& prefix,
|
|
wxFFile *fileTemp,
|
|
bool *deleteOnClose)
|
|
{
|
|
return wxCreateTempImpl(prefix, WXFILEARGS(NULL, fileTemp), deleteOnClose);
|
|
}
|
|
|
|
bool wxCreateTempFile(const wxString& prefix,
|
|
wxFFile *fileTemp,
|
|
wxString *name)
|
|
{
|
|
return wxCreateTempImpl(prefix, WXFILEARGS(NULL, fileTemp), name);
|
|
|
|
}
|
|
|
|
void wxFileName::AssignTempFileName(const wxString& prefix, wxFFile *fileTemp)
|
|
{
|
|
wxAssignTempImpl(this, prefix, WXFILEARGS(NULL, fileTemp));
|
|
}
|
|
|
|
/* static */
|
|
wxString
|
|
wxFileName::CreateTempFileName(const wxString& prefix, wxFFile *fileTemp)
|
|
{
|
|
return wxCreateTempFileName(prefix, fileTemp);
|
|
}
|
|
|
|
#endif // wxUSE_FFILE
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// directory operations
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// helper of GetTempDir(): check if the given directory exists and return it if
|
|
// it does or an empty string otherwise
|
|
namespace
|
|
{
|
|
|
|
wxString CheckIfDirExists(const wxString& dir)
|
|
{
|
|
return wxFileName::DirExists(dir) ? dir : wxString();
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
wxString wxFileName::GetTempDir()
|
|
{
|
|
// first try getting it from environment: this allows overriding the values
|
|
// used by default if the user wants to create temporary files in another
|
|
// directory
|
|
wxString dir = CheckIfDirExists(wxGetenv("TMPDIR"));
|
|
if ( dir.empty() )
|
|
{
|
|
dir = CheckIfDirExists(wxGetenv("TMP"));
|
|
if ( dir.empty() )
|
|
dir = CheckIfDirExists(wxGetenv("TEMP"));
|
|
}
|
|
|
|
// if no environment variables are set, use the system default
|
|
if ( dir.empty() )
|
|
{
|
|
#if defined(__WXWINCE__)
|
|
dir = CheckIfDirExists(wxT("\\temp"));
|
|
#elif defined(__WINDOWS__) && !defined(__WXMICROWIN__)
|
|
if ( !::GetTempPath(MAX_PATH, wxStringBuffer(dir, MAX_PATH + 1)) )
|
|
{
|
|
wxLogLastError(wxT("GetTempPath"));
|
|
}
|
|
#elif defined(__WXMAC__) && wxOSX_USE_CARBON
|
|
dir = wxMacFindFolderNoSeparator(short(kOnSystemDisk), kTemporaryFolderType, kCreateFolder);
|
|
#endif // systems with native way
|
|
}
|
|
else // we got directory from an environment variable
|
|
{
|
|
// remove any trailing path separators, we don't want to ever return
|
|
// them from this function for consistency
|
|
const size_t lastNonSep = dir.find_last_not_of(GetPathSeparators());
|
|
if ( lastNonSep == wxString::npos )
|
|
{
|
|
// the string consists entirely of separators, leave only one
|
|
dir = GetPathSeparator();
|
|
}
|
|
else
|
|
{
|
|
dir.erase(lastNonSep + 1);
|
|
}
|
|
}
|
|
|
|
// fall back to hard coded value
|
|
if ( dir.empty() )
|
|
{
|
|
#ifdef __UNIX_LIKE__
|
|
dir = CheckIfDirExists("/tmp");
|
|
if ( dir.empty() )
|
|
#endif // __UNIX_LIKE__
|
|
dir = ".";
|
|
}
|
|
|
|
return dir;
|
|
}
|
|
|
|
bool wxFileName::Mkdir( int perm, int flags ) const
|
|
{
|
|
return wxFileName::Mkdir(GetPath(), perm, flags);
|
|
}
|
|
|
|
bool wxFileName::Mkdir( const wxString& dir, int perm, int flags )
|
|
{
|
|
if ( flags & wxPATH_MKDIR_FULL )
|
|
{
|
|
// split the path in components
|
|
wxFileName filename;
|
|
filename.AssignDir(dir);
|
|
|
|
wxString currPath;
|
|
if ( filename.HasVolume())
|
|
{
|
|
currPath << wxGetVolumeString(filename.GetVolume(), wxPATH_NATIVE);
|
|
}
|
|
|
|
wxArrayString dirs = filename.GetDirs();
|
|
size_t count = dirs.GetCount();
|
|
for ( size_t i = 0; i < count; i++ )
|
|
{
|
|
if ( i > 0 || filename.IsAbsolute() )
|
|
currPath += wxFILE_SEP_PATH;
|
|
currPath += dirs[i];
|
|
|
|
if (!DirExists(currPath))
|
|
{
|
|
if (!wxMkdir(currPath, perm))
|
|
{
|
|
// no need to try creating further directories
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return ::wxMkdir( dir, perm );
|
|
}
|
|
|
|
bool wxFileName::Rmdir(int flags) const
|
|
{
|
|
return wxFileName::Rmdir( GetPath(), flags );
|
|
}
|
|
|
|
bool wxFileName::Rmdir(const wxString& dir, int flags)
|
|
{
|
|
#ifdef __WINDOWS__
|
|
if ( flags & wxPATH_RMDIR_RECURSIVE )
|
|
{
|
|
// SHFileOperation needs double null termination string
|
|
// but without separator at the end of the path
|
|
wxString path(dir);
|
|
if ( path.Last() == wxFILE_SEP_PATH )
|
|
path.RemoveLast();
|
|
path += wxT('\0');
|
|
|
|
SHFILEOPSTRUCT fileop;
|
|
wxZeroMemory(fileop);
|
|
fileop.wFunc = FO_DELETE;
|
|
fileop.pFrom = path.t_str();
|
|
fileop.fFlags = FOF_SILENT | FOF_NOCONFIRMATION;
|
|
#ifndef __WXWINCE__
|
|
// FOF_NOERRORUI is not defined in WinCE
|
|
fileop.fFlags |= FOF_NOERRORUI;
|
|
#endif
|
|
|
|
int ret = SHFileOperation(&fileop);
|
|
if ( ret != 0 )
|
|
{
|
|
// SHFileOperation may return non-Win32 error codes, so the error
|
|
// message can be incorrect
|
|
wxLogApiError(wxT("SHFileOperation"), ret);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else if ( flags & wxPATH_RMDIR_FULL )
|
|
#else // !__WINDOWS__
|
|
if ( flags != 0 ) // wxPATH_RMDIR_FULL or wxPATH_RMDIR_RECURSIVE
|
|
#endif // !__WINDOWS__
|
|
{
|
|
#ifndef __WINDOWS__
|
|
if ( flags & wxPATH_RMDIR_RECURSIVE )
|
|
{
|
|
// When deleting the tree recursively, we are supposed to delete
|
|
// this directory itself even when it is a symlink -- but without
|
|
// following it. Do it here as wxRmdir() would simply follow if
|
|
// called for a symlink.
|
|
if ( wxFileName::Exists(dir, wxFILE_EXISTS_SYMLINK) )
|
|
{
|
|
return wxRemoveFile(dir);
|
|
}
|
|
}
|
|
#endif // !__WINDOWS__
|
|
|
|
wxString path(dir);
|
|
if ( path.Last() != wxFILE_SEP_PATH )
|
|
path += wxFILE_SEP_PATH;
|
|
|
|
wxDir d(path);
|
|
|
|
if ( !d.IsOpened() )
|
|
return false;
|
|
|
|
wxString filename;
|
|
|
|
// First delete all subdirectories: notice that we don't follow
|
|
// symbolic links, potentially leading outside this directory, to avoid
|
|
// unpleasant surprises.
|
|
bool cont = d.GetFirst(&filename, wxString(),
|
|
wxDIR_DIRS | wxDIR_HIDDEN | wxDIR_NO_FOLLOW);
|
|
while ( cont )
|
|
{
|
|
wxFileName::Rmdir(path + filename, flags);
|
|
cont = d.GetNext(&filename);
|
|
}
|
|
|
|
#ifndef __WINDOWS__
|
|
if ( flags & wxPATH_RMDIR_RECURSIVE )
|
|
{
|
|
// Delete all files too and, for the same reasons as above, don't
|
|
// follow symlinks which could refer to the files outside of this
|
|
// directory and just delete the symlinks themselves.
|
|
cont = d.GetFirst(&filename, wxString(),
|
|
wxDIR_FILES | wxDIR_HIDDEN | wxDIR_NO_FOLLOW);
|
|
while ( cont )
|
|
{
|
|
::wxRemoveFile(path + filename);
|
|
cont = d.GetNext(&filename);
|
|
}
|
|
}
|
|
#endif // !__WINDOWS__
|
|
}
|
|
|
|
return ::wxRmdir(dir);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// path normalization
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool wxFileName::Normalize(int flags,
|
|
const wxString& cwd,
|
|
wxPathFormat format)
|
|
{
|
|
// deal with env vars renaming first as this may seriously change the path
|
|
if ( flags & wxPATH_NORM_ENV_VARS )
|
|
{
|
|
wxString pathOrig = GetFullPath(format);
|
|
wxString path = wxExpandEnvVars(pathOrig);
|
|
if ( path != pathOrig )
|
|
{
|
|
Assign(path);
|
|
}
|
|
}
|
|
|
|
// the existing path components
|
|
wxArrayString dirs = GetDirs();
|
|
|
|
// the path to prepend in front to make the path absolute
|
|
wxFileName curDir;
|
|
|
|
format = GetFormat(format);
|
|
|
|
// set up the directory to use for making the path absolute later
|
|
if ( (flags & wxPATH_NORM_ABSOLUTE) && !IsAbsolute(format) )
|
|
{
|
|
if ( cwd.empty() )
|
|
{
|
|
curDir.AssignCwd(GetVolume());
|
|
}
|
|
else // cwd provided
|
|
{
|
|
curDir.AssignDir(cwd);
|
|
}
|
|
}
|
|
|
|
// handle ~ stuff under Unix only
|
|
if ( (format == wxPATH_UNIX) && (flags & wxPATH_NORM_TILDE) && m_relative )
|
|
{
|
|
if ( !dirs.IsEmpty() )
|
|
{
|
|
wxString dir = dirs[0u];
|
|
if ( !dir.empty() && dir[0u] == wxT('~') )
|
|
{
|
|
// to make the path absolute use the home directory
|
|
curDir.AssignDir(wxGetUserHome(dir.c_str() + 1));
|
|
dirs.RemoveAt(0u);
|
|
}
|
|
}
|
|
}
|
|
|
|
// transform relative path into abs one
|
|
if ( curDir.IsOk() )
|
|
{
|
|
// this path may be relative because it doesn't have the volume name
|
|
// and still have m_relative=true; in this case we shouldn't modify
|
|
// our directory components but just set the current volume
|
|
if ( !HasVolume() && curDir.HasVolume() )
|
|
{
|
|
SetVolume(curDir.GetVolume());
|
|
|
|
if ( !m_relative )
|
|
{
|
|
// yes, it was the case - we don't need curDir then
|
|
curDir.Clear();
|
|
}
|
|
}
|
|
|
|
// finally, prepend curDir to the dirs array
|
|
wxArrayString dirsNew = curDir.GetDirs();
|
|
WX_PREPEND_ARRAY(dirs, dirsNew);
|
|
|
|
// if we used e.g. tilde expansion previously and wxGetUserHome didn't
|
|
// return for some reason an absolute path, then curDir maybe not be absolute!
|
|
if ( !curDir.m_relative )
|
|
{
|
|
// we have prepended an absolute path and thus we are now an absolute
|
|
// file name too
|
|
m_relative = false;
|
|
}
|
|
// else if (flags & wxPATH_NORM_ABSOLUTE):
|
|
// should we warn the user that we didn't manage to make the path absolute?
|
|
}
|
|
|
|
// 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.empty() )
|
|
{
|
|
// We have more ".." than directory components so far.
|
|
// Don't treat this as an error as the path could have been
|
|
// entered by user so try to handle it reasonably: if the
|
|
// path is absolute, just ignore the extra ".." because
|
|
// "/.." is the same as "/". Otherwise, i.e. for relative
|
|
// paths, keep ".." unchanged because removing it would
|
|
// modify the file a relative path refers to.
|
|
if ( !m_relative )
|
|
continue;
|
|
|
|
}
|
|
else // Normal case, go one step up.
|
|
{
|
|
m_dirs.pop_back();
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_dirs.Add(dir);
|
|
}
|
|
|
|
#if defined(__WIN32__) && !defined(__WXWINCE__) && wxUSE_OLE
|
|
if ( (flags & wxPATH_NORM_SHORTCUT) )
|
|
{
|
|
wxString filename;
|
|
if (GetShortcutTarget(GetFullPath(format), filename))
|
|
{
|
|
m_relative = false;
|
|
Assign(filename);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(__WIN32__)
|
|
if ( (flags & wxPATH_NORM_LONG) && (format == wxPATH_DOS) )
|
|
{
|
|
Assign(GetLongPath());
|
|
}
|
|
#endif // Win32
|
|
|
|
// Change case (this should be kept at the end of the function, to ensure
|
|
// that the path doesn't change any more after we normalize its case)
|
|
if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) )
|
|
{
|
|
m_volume.MakeLower();
|
|
m_name.MakeLower();
|
|
m_ext.MakeLower();
|
|
|
|
// directory entries must be made lower case as well
|
|
count = m_dirs.GetCount();
|
|
for ( size_t i = 0; i < count; i++ )
|
|
{
|
|
m_dirs[i].MakeLower();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifndef __WXWINCE__
|
|
bool wxFileName::ReplaceEnvVariable(const wxString& envname,
|
|
const wxString& replacementFmtString,
|
|
wxPathFormat format)
|
|
{
|
|
// look into stringForm for the contents of the given environment variable
|
|
wxString val;
|
|
if (envname.empty() ||
|
|
!wxGetEnv(envname, &val))
|
|
return false;
|
|
if (val.empty())
|
|
return false;
|
|
|
|
wxString stringForm = GetPath(wxPATH_GET_VOLUME, format);
|
|
// do not touch the file name and the extension
|
|
|
|
wxString replacement = wxString::Format(replacementFmtString, envname);
|
|
stringForm.Replace(val, replacement);
|
|
|
|
// Now assign ourselves the modified path:
|
|
Assign(stringForm, GetFullName(), format);
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
bool wxFileName::ReplaceHomeDir(wxPathFormat format)
|
|
{
|
|
wxString homedir = wxGetHomeDir();
|
|
if (homedir.empty())
|
|
return false;
|
|
|
|
wxString stringForm = GetPath(wxPATH_GET_VOLUME, format);
|
|
// do not touch the file name and the extension
|
|
|
|
stringForm.Replace(homedir, "~");
|
|
|
|
// Now assign ourselves the modified path:
|
|
Assign(stringForm, GetFullName(), format);
|
|
|
|
return true;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// get the shortcut target
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// WinCE (3) doesn't have CLSID_ShellLink, IID_IShellLink definitions.
|
|
// The .lnk file is a plain text file so it should be easy to
|
|
// make it work. Hint from Google Groups:
|
|
// "If you open up a lnk file, you'll see a
|
|
// number, followed by a pound sign (#), followed by more text. The
|
|
// number is the number of characters that follows the pound sign. The
|
|
// characters after the pound sign are the command line (which _can_
|
|
// include arguments) to be executed. Any path (e.g. \windows\program
|
|
// files\myapp.exe) that includes spaces needs to be enclosed in
|
|
// quotation marks."
|
|
|
|
#if defined(__WIN32__) && !defined(__WXWINCE__) && wxUSE_OLE
|
|
|
|
bool wxFileName::GetShortcutTarget(const wxString& shortcutPath,
|
|
wxString& targetFilename,
|
|
wxString* arguments) const
|
|
{
|
|
wxString path, file, ext;
|
|
wxFileName::SplitPath(shortcutPath, & path, & file, & ext);
|
|
|
|
HRESULT hres;
|
|
IShellLink* psl;
|
|
bool success = false;
|
|
|
|
// Assume it's not a shortcut if it doesn't end with lnk
|
|
if (ext.CmpNoCase(wxT("lnk"))!=0)
|
|
return false;
|
|
|
|
// Ensure OLE is initialized.
|
|
wxOleInitializer oleInit;
|
|
|
|
// create a ShellLink object
|
|
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_IShellLink, (LPVOID*) &psl);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
IPersistFile* ppf;
|
|
hres = psl->QueryInterface( IID_IPersistFile, (LPVOID *) &ppf);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
WCHAR wsz[MAX_PATH];
|
|
|
|
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, shortcutPath.mb_str(), -1, wsz,
|
|
MAX_PATH);
|
|
|
|
hres = ppf->Load(wsz, 0);
|
|
ppf->Release();
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
wxChar buf[2048];
|
|
// Wrong prototype in early versions
|
|
#if defined(__MINGW32__) && !wxCHECK_W32API_VERSION(2, 2)
|
|
psl->GetPath((CHAR*) buf, 2048, NULL, SLGP_UNCPRIORITY);
|
|
#else
|
|
psl->GetPath(buf, 2048, NULL, SLGP_UNCPRIORITY);
|
|
#endif
|
|
targetFilename = wxString(buf);
|
|
success = (shortcutPath != targetFilename);
|
|
|
|
psl->GetArguments(buf, 2048);
|
|
wxString args(buf);
|
|
if (!args.empty() && arguments)
|
|
{
|
|
*arguments = args;
|
|
}
|
|
}
|
|
}
|
|
|
|
psl->Release();
|
|
}
|
|
return success;
|
|
}
|
|
|
|
#endif // __WIN32__ && !__WXWINCE__
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// absolute/relative paths
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool wxFileName::IsAbsolute(wxPathFormat format) const
|
|
{
|
|
// unix paths beginning with ~ are reported as being absolute
|
|
if ( format == wxPATH_UNIX )
|
|
{
|
|
if ( !m_dirs.IsEmpty() )
|
|
{
|
|
wxString dir = m_dirs[0u];
|
|
|
|
if (!dir.empty() && dir[0u] == wxT('~'))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// if our path doesn't start with a path separator, it's not an absolute
|
|
// path
|
|
if ( m_relative )
|
|
return false;
|
|
|
|
if ( !GetVolumeSeparator(format).empty() )
|
|
{
|
|
// this format has volumes and an absolute path must have one, it's not
|
|
// enough to have the full path to be an absolute file under Windows
|
|
if ( GetVolume().empty() )
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxFileName::MakeRelativeTo(const wxString& pathBase, wxPathFormat format)
|
|
{
|
|
wxFileName fnBase = wxFileName::DirName(pathBase, format);
|
|
|
|
// get cwd only once - small time saving
|
|
wxString cwd = wxGetCwd();
|
|
|
|
// Normalize the paths but avoid changing the case or turning a shortcut
|
|
// into a file that it points to.
|
|
const int normFlags = wxPATH_NORM_ALL &
|
|
~(wxPATH_NORM_CASE | wxPATH_NORM_SHORTCUT);
|
|
Normalize(normFlags, cwd, format);
|
|
fnBase.Normalize(normFlags, 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);
|
|
}
|
|
|
|
if ( format == wxPATH_UNIX || format == wxPATH_DOS )
|
|
{
|
|
// a directory made relative with respect to itself is '.' under Unix
|
|
// and DOS, by definition (but we don't have to insert "./" for the
|
|
// files)
|
|
if ( m_dirs.IsEmpty() && IsDir() )
|
|
{
|
|
m_dirs.Add(wxT('.'));
|
|
}
|
|
}
|
|
|
|
m_relative = true;
|
|
|
|
// we were modified
|
|
return true;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// filename kind tests
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool wxFileName::SameAs(const wxFileName& filepath, wxPathFormat format) const
|
|
{
|
|
wxFileName fn1 = *this,
|
|
fn2 = filepath;
|
|
|
|
// get cwd only once - small time saving
|
|
wxString cwd = wxGetCwd();
|
|
fn1.Normalize(wxPATH_NORM_ALL | wxPATH_NORM_CASE, cwd, format);
|
|
fn2.Normalize(wxPATH_NORM_ALL | wxPATH_NORM_CASE, cwd, format);
|
|
|
|
if ( fn1.GetFullPath() == fn2.GetFullPath() )
|
|
return true;
|
|
|
|
#ifdef wxHAVE_LSTAT
|
|
wxStructStat st1, st2;
|
|
if ( StatAny(st1, fn1) && StatAny(st2, fn2) )
|
|
{
|
|
if ( st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev )
|
|
return true;
|
|
}
|
|
//else: It's not an error if one or both files don't exist.
|
|
#endif // wxHAVE_LSTAT
|
|
|
|
return false;
|
|
}
|
|
|
|
/* static */
|
|
bool wxFileName::IsCaseSensitive( wxPathFormat format )
|
|
{
|
|
// only Unix filenames are truly case-sensitive
|
|
return GetFormat(format) == wxPATH_UNIX;
|
|
}
|
|
|
|
/* static */
|
|
wxString wxFileName::GetForbiddenChars(wxPathFormat format)
|
|
{
|
|
// Inits to forbidden characters that are common to (almost) all platforms.
|
|
wxString strForbiddenChars = wxT("*?");
|
|
|
|
// If asserts, wxPathFormat has been changed. In case of a new path format
|
|
// addition, the following code might have to be updated.
|
|
wxCOMPILE_TIME_ASSERT(wxPATH_MAX == 5, wxPathFormatChanged);
|
|
switch ( GetFormat(format) )
|
|
{
|
|
default :
|
|
wxFAIL_MSG( wxT("Unknown path format") );
|
|
wxFALLTHROUGH;
|
|
|
|
case wxPATH_UNIX:
|
|
break;
|
|
|
|
case wxPATH_MAC:
|
|
// On a Mac even names with * and ? are allowed (Tested with OS
|
|
// 9.2.1 and OS X 10.2.5)
|
|
strForbiddenChars.clear();
|
|
break;
|
|
|
|
case wxPATH_DOS:
|
|
strForbiddenChars += wxT("\\/:\"<>|");
|
|
break;
|
|
|
|
case wxPATH_VMS:
|
|
break;
|
|
}
|
|
|
|
return strForbiddenChars;
|
|
}
|
|
|
|
/* static */
|
|
wxString wxFileName::GetVolumeSeparator(wxPathFormat WXUNUSED_IN_WINCE(format))
|
|
{
|
|
#ifdef __WXWINCE__
|
|
return wxEmptyString;
|
|
#else
|
|
wxString sepVol;
|
|
|
|
if ( (GetFormat(format) == wxPATH_DOS) ||
|
|
(GetFormat(format) == wxPATH_VMS) )
|
|
{
|
|
sepVol = wxFILE_SEP_DSK;
|
|
}
|
|
//else: leave empty
|
|
|
|
return sepVol;
|
|
#endif
|
|
}
|
|
|
|
/* 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( wxT("Unknown wxPATH_XXX style") );
|
|
wxFALLTHROUGH;
|
|
|
|
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 */
|
|
wxString wxFileName::GetPathTerminators(wxPathFormat format)
|
|
{
|
|
format = GetFormat(format);
|
|
|
|
// under VMS the end of the path is ']', not the path separator used to
|
|
// separate the components
|
|
return format == wxPATH_VMS ? wxString(wxT(']')) : GetPathSeparators(format);
|
|
}
|
|
|
|
/* static */
|
|
bool wxFileName::IsPathSeparator(wxChar ch, wxPathFormat format)
|
|
{
|
|
// wxString::Find() doesn't work as expected with NUL - it will always find
|
|
// it, so test for it separately
|
|
return ch != wxT('\0') && GetPathSeparators(format).Find(ch) != wxNOT_FOUND;
|
|
}
|
|
|
|
/* static */
|
|
bool
|
|
wxFileName::IsMSWUniqueVolumeNamePath(const wxString& path, wxPathFormat format)
|
|
{
|
|
// return true if the format used is the DOS/Windows one and the string begins
|
|
// with a Windows unique volume name ("\\?\Volume{guid}\")
|
|
return format == wxPATH_DOS &&
|
|
path.length() >= wxMSWUniqueVolumePrefixLength &&
|
|
path.StartsWith(wxS("\\\\?\\Volume{")) &&
|
|
path[wxMSWUniqueVolumePrefixLength - 1] == wxFILE_SEP_PATH_DOS;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// path components manipulation
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/* static */ bool wxFileName::IsValidDirComponent(const wxString& dir)
|
|
{
|
|
if ( dir.empty() )
|
|
{
|
|
wxFAIL_MSG( wxT("empty directory passed to wxFileName::InsertDir()") );
|
|
|
|
return false;
|
|
}
|
|
|
|
const size_t len = dir.length();
|
|
for ( size_t n = 0; n < len; n++ )
|
|
{
|
|
if ( dir[n] == GetVolumeSeparator() || IsPathSeparator(dir[n]) )
|
|
{
|
|
wxFAIL_MSG( wxT("invalid directory component in wxFileName") );
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxFileName::AppendDir( const wxString& dir )
|
|
{
|
|
if (!IsValidDirComponent(dir))
|
|
return false;
|
|
m_dirs.Add(dir);
|
|
return true;
|
|
}
|
|
|
|
void wxFileName::PrependDir( const wxString& dir )
|
|
{
|
|
InsertDir(0, dir);
|
|
}
|
|
|
|
bool wxFileName::InsertDir(size_t before, const wxString& dir)
|
|
{
|
|
if (!IsValidDirComponent(dir))
|
|
return false;
|
|
m_dirs.Insert(dir, before);
|
|
return true;
|
|
}
|
|
|
|
void wxFileName::RemoveDir(size_t pos)
|
|
{
|
|
m_dirs.RemoveAt(pos);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// accessors
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxFileName::SetFullName(const wxString& fullname)
|
|
{
|
|
SplitPath(fullname, NULL /* no volume */, NULL /* no path */,
|
|
&m_name, &m_ext, &m_hasExt);
|
|
}
|
|
|
|
wxString wxFileName::GetFullName() const
|
|
{
|
|
wxString fullname = m_name;
|
|
if ( m_hasExt )
|
|
{
|
|
fullname << wxFILE_SEP_EXT << m_ext;
|
|
}
|
|
|
|
return fullname;
|
|
}
|
|
|
|
wxString wxFileName::GetPath( int flags, wxPathFormat format ) const
|
|
{
|
|
format = GetFormat( format );
|
|
|
|
wxString fullpath;
|
|
|
|
// return the volume with the path as well if requested
|
|
if ( flags & wxPATH_GET_VOLUME )
|
|
{
|
|
fullpath += wxGetVolumeString(GetVolume(), format);
|
|
}
|
|
|
|
// the leading character
|
|
switch ( format )
|
|
{
|
|
case wxPATH_MAC:
|
|
if ( m_relative )
|
|
fullpath += wxFILE_SEP_PATH_MAC;
|
|
break;
|
|
|
|
case wxPATH_DOS:
|
|
if ( !m_relative )
|
|
fullpath += wxFILE_SEP_PATH_DOS;
|
|
break;
|
|
|
|
default:
|
|
wxFAIL_MSG( wxT("Unknown path format") );
|
|
wxFALLTHROUGH;
|
|
|
|
case wxPATH_UNIX:
|
|
if ( !m_relative )
|
|
{
|
|
fullpath += wxFILE_SEP_PATH_UNIX;
|
|
}
|
|
break;
|
|
|
|
case wxPATH_VMS:
|
|
// no leading character here but use this place to unset
|
|
// wxPATH_GET_SEPARATOR flag: under VMS it doesn't make sense
|
|
// as, if I understand correctly, there should never be a dot
|
|
// before the closing bracket
|
|
flags &= ~wxPATH_GET_SEPARATOR;
|
|
}
|
|
|
|
if ( m_dirs.empty() )
|
|
{
|
|
// there is nothing more
|
|
return fullpath;
|
|
}
|
|
|
|
// then concatenate all the path components using the path separator
|
|
if ( format == wxPATH_VMS )
|
|
{
|
|
fullpath += wxT('[');
|
|
}
|
|
|
|
const size_t dirCount = m_dirs.GetCount();
|
|
for ( size_t i = 0; i < dirCount; i++ )
|
|
{
|
|
switch (format)
|
|
{
|
|
case wxPATH_MAC:
|
|
if ( m_dirs[i] == wxT(".") )
|
|
{
|
|
// skip appending ':', this shouldn't be done in this
|
|
// case as "::" is interpreted as ".." under Unix
|
|
continue;
|
|
}
|
|
|
|
// convert back from ".." to nothing
|
|
if ( !m_dirs[i].IsSameAs(wxT("..")) )
|
|
fullpath += m_dirs[i];
|
|
break;
|
|
|
|
default:
|
|
wxFAIL_MSG( wxT("Unexpected path format") );
|
|
wxFALLTHROUGH;
|
|
|
|
case wxPATH_DOS:
|
|
case wxPATH_UNIX:
|
|
fullpath += m_dirs[i];
|
|
break;
|
|
|
|
case wxPATH_VMS:
|
|
// TODO: What to do with ".." under VMS
|
|
|
|
// convert back from ".." to nothing
|
|
if ( !m_dirs[i].IsSameAs(wxT("..")) )
|
|
fullpath += m_dirs[i];
|
|
break;
|
|
}
|
|
|
|
if ( (flags & wxPATH_GET_SEPARATOR) || (i != dirCount - 1) )
|
|
fullpath += GetPathSeparator(format);
|
|
}
|
|
|
|
if ( format == wxPATH_VMS )
|
|
{
|
|
fullpath += wxT(']');
|
|
}
|
|
|
|
return fullpath;
|
|
}
|
|
|
|
wxString wxFileName::GetFullPath( wxPathFormat format ) const
|
|
{
|
|
// we already have a function to get the path
|
|
wxString fullpath = GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR,
|
|
format);
|
|
|
|
// now just add the file name and extension to it
|
|
fullpath += GetFullName();
|
|
|
|
return fullpath;
|
|
}
|
|
|
|
// Return the short form of the path (returns identity on non-Windows platforms)
|
|
wxString wxFileName::GetShortPath() const
|
|
{
|
|
wxString path(GetFullPath());
|
|
|
|
#if defined(__WINDOWS__) && defined(__WIN32__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
|
|
DWORD sz = ::GetShortPathName(path.t_str(), NULL, 0);
|
|
if ( sz != 0 )
|
|
{
|
|
wxString pathOut;
|
|
if ( ::GetShortPathName
|
|
(
|
|
path.t_str(),
|
|
wxStringBuffer(pathOut, sz),
|
|
sz
|
|
) != 0 )
|
|
{
|
|
return pathOut;
|
|
}
|
|
}
|
|
#endif // Windows
|
|
|
|
return path;
|
|
}
|
|
|
|
// 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(__WXWINCE__) && !defined(__WXMICROWIN__)
|
|
|
|
#if wxUSE_DYNLIB_CLASS
|
|
typedef DWORD (WINAPI *GET_LONG_PATH_NAME)(const wxChar *, wxChar *, DWORD);
|
|
|
|
// this is MT-safe as in the worst case we're going to resolve the function
|
|
// twice -- but as the result is the same in both threads, it's ok
|
|
static GET_LONG_PATH_NAME s_pfnGetLongPathName = NULL;
|
|
if ( !s_pfnGetLongPathName )
|
|
{
|
|
static bool s_triedToLoad = false;
|
|
|
|
if ( !s_triedToLoad )
|
|
{
|
|
s_triedToLoad = true;
|
|
|
|
wxDynamicLibrary dllKernel(wxT("kernel32"));
|
|
|
|
const wxChar* GetLongPathName = wxT("GetLongPathName")
|
|
#if wxUSE_UNICODE
|
|
wxT("W");
|
|
#else // ANSI
|
|
wxT("A");
|
|
#endif // Unicode/ANSI
|
|
|
|
if ( dllKernel.HasSymbol(GetLongPathName) )
|
|
{
|
|
s_pfnGetLongPathName = (GET_LONG_PATH_NAME)
|
|
dllKernel.GetSymbol(GetLongPathName);
|
|
}
|
|
|
|
// note that kernel32.dll can be unloaded, it stays in memory
|
|
// anyhow as all Win32 programs link to it and so it's safe to call
|
|
// GetLongPathName() even after unloading it
|
|
}
|
|
}
|
|
|
|
if ( s_pfnGetLongPathName )
|
|
{
|
|
DWORD dwSize = (*s_pfnGetLongPathName)(path.t_str(), NULL, 0);
|
|
if ( dwSize > 0 )
|
|
{
|
|
if ( (*s_pfnGetLongPathName)
|
|
(
|
|
path.t_str(),
|
|
wxStringBuffer(pathOut, dwSize),
|
|
dwSize
|
|
) != 0 )
|
|
{
|
|
return pathOut;
|
|
}
|
|
}
|
|
}
|
|
#endif // wxUSE_DYNLIB_CLASS
|
|
|
|
// 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;
|
|
|
|
if ( HasVolume() )
|
|
pathOut = GetVolume() +
|
|
GetVolumeSeparator(wxPATH_DOS) +
|
|
GetPathSeparator(wxPATH_DOS);
|
|
else
|
|
pathOut.clear();
|
|
|
|
wxArrayString dirs = GetDirs();
|
|
dirs.Add(GetFullName());
|
|
|
|
wxString tmpPath;
|
|
|
|
size_t count = dirs.GetCount();
|
|
for ( size_t i = 0; i < count; i++ )
|
|
{
|
|
const wxString& dir = dirs[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 + dir;
|
|
|
|
// We must not process "." or ".." here as they would be (unexpectedly)
|
|
// replaced by the corresponding directory names so just leave them
|
|
// alone
|
|
//
|
|
// And we can't pass a drive and root dir to FindFirstFile (VZ: why?)
|
|
if ( tmpPath.empty() || dir == '.' || dir == ".." ||
|
|
tmpPath.Last() == GetVolumeSeparator(wxPATH_DOS) )
|
|
{
|
|
tmpPath += wxFILE_SEP_PATH;
|
|
pathOut = tmpPath;
|
|
continue;
|
|
}
|
|
|
|
hFind = ::FindFirstFile(tmpPath.t_str(), &findFileData);
|
|
if (hFind == INVALID_HANDLE_VALUE)
|
|
{
|
|
// Error: most likely reason is that path doesn't exist, so
|
|
// append any unprocessed parts and return
|
|
for ( i += 1; i < count; i++ )
|
|
tmpPath += wxFILE_SEP_PATH + dirs[i];
|
|
|
|
return tmpPath;
|
|
}
|
|
|
|
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(__WINDOWS__) || defined(__DOS__)
|
|
format = wxPATH_DOS;
|
|
#elif defined(__VMS)
|
|
format = wxPATH_VMS;
|
|
#else
|
|
format = wxPATH_UNIX;
|
|
#endif
|
|
}
|
|
return format;
|
|
}
|
|
|
|
#ifdef wxHAS_FILESYSTEM_VOLUMES
|
|
|
|
/* static */
|
|
wxString wxFileName::GetVolumeString(char drive, int flags)
|
|
{
|
|
wxASSERT_MSG( !(flags & ~wxPATH_GET_SEPARATOR), "invalid flag specified" );
|
|
|
|
wxString vol(drive);
|
|
vol += wxFILE_SEP_DSK;
|
|
if ( flags & wxPATH_GET_SEPARATOR )
|
|
vol += wxFILE_SEP_PATH;
|
|
|
|
return vol;
|
|
}
|
|
|
|
#endif // wxHAS_FILESYSTEM_VOLUMES
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// path splitting function
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/* static */
|
|
void
|
|
wxFileName::SplitVolume(const wxString& fullpathWithVolume,
|
|
wxString *pstrVolume,
|
|
wxString *pstrPath,
|
|
wxPathFormat format)
|
|
{
|
|
format = GetFormat(format);
|
|
|
|
wxString fullpath = fullpathWithVolume;
|
|
|
|
if ( IsMSWUniqueVolumeNamePath(fullpath, format) )
|
|
{
|
|
// special Windows unique volume names hack: transform
|
|
// \\?\Volume{guid}\path into Volume{guid}:path
|
|
// note: this check must be done before the check for UNC path
|
|
|
|
// we know the last backslash from the unique volume name is located
|
|
// there from IsMSWUniqueVolumeNamePath
|
|
fullpath[wxMSWUniqueVolumePrefixLength - 1] = wxFILE_SEP_DSK;
|
|
|
|
// paths starting with a unique volume name should always be absolute
|
|
fullpath.insert(wxMSWUniqueVolumePrefixLength, 1, wxFILE_SEP_PATH_DOS);
|
|
|
|
// remove the leading "\\?\" part
|
|
fullpath.erase(0, 4);
|
|
}
|
|
else if ( IsUNCPath(fullpath, format) )
|
|
{
|
|
// special Windows UNC paths hack: transform \\share\path into share:path
|
|
|
|
fullpath.erase(0, 2);
|
|
|
|
size_t posFirstSlash =
|
|
fullpath.find_first_of(GetPathTerminators(format));
|
|
if ( posFirstSlash != wxString::npos )
|
|
{
|
|
fullpath[posFirstSlash] = wxFILE_SEP_DSK;
|
|
|
|
// UNC paths are always absolute, right? (FIXME)
|
|
fullpath.insert(posFirstSlash + 1, 1, wxFILE_SEP_PATH_DOS);
|
|
}
|
|
}
|
|
|
|
// We separate the volume here
|
|
if ( format == wxPATH_DOS || format == wxPATH_VMS )
|
|
{
|
|
wxString sepVol = GetVolumeSeparator(format);
|
|
|
|
// we have to exclude the case of a colon in the very beginning of the
|
|
// string as it can't be a volume separator (nor can this be a valid
|
|
// DOS file name at all but we'll leave dealing with this to our caller)
|
|
size_t posFirstColon = fullpath.find_first_of(sepVol);
|
|
if ( posFirstColon && 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());
|
|
}
|
|
}
|
|
|
|
if ( pstrPath )
|
|
*pstrPath = fullpath;
|
|
}
|
|
|
|
/* static */
|
|
void wxFileName::SplitPath(const wxString& fullpathWithVolume,
|
|
wxString *pstrVolume,
|
|
wxString *pstrPath,
|
|
wxString *pstrName,
|
|
wxString *pstrExt,
|
|
bool *hasExt,
|
|
wxPathFormat format)
|
|
{
|
|
format = GetFormat(format);
|
|
|
|
wxString fullpath;
|
|
SplitVolume(fullpathWithVolume, pstrVolume, &fullpath, format);
|
|
|
|
// 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(GetPathTerminators(format));
|
|
|
|
// check whether this dot occurs at the very beginning of a path component
|
|
if ( (posLastDot != wxString::npos) &&
|
|
(posLastDot == 0 ||
|
|
IsPathSeparator(fullpath[posLastDot - 1]) ||
|
|
(format == wxPATH_VMS && fullpath[posLastDot - 1] == wxT(']'))) )
|
|
{
|
|
// dot may be (and commonly -- at least under Unix -- 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;
|
|
|
|
// this rule does not apply to mac since we do not start with colons (sep)
|
|
// except for relative paths
|
|
if ( !len && format != wxPATH_MAC)
|
|
len++;
|
|
|
|
*pstrPath = fullpath.Left(len);
|
|
|
|
// special VMS hack: remove the initial bracket
|
|
if ( format == wxPATH_VMS )
|
|
{
|
|
if ( (*pstrPath)[0u] == wxT('[') )
|
|
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);
|
|
}
|
|
|
|
// finally deal with the extension here: we have an added complication that
|
|
// extension may be empty (but present) as in "foo." where trailing dot
|
|
// indicates the empty extension at the end -- and hence we must remember
|
|
// that we have it independently of pstrExt
|
|
if ( posLastDot == wxString::npos )
|
|
{
|
|
// no extension
|
|
if ( pstrExt )
|
|
pstrExt->clear();
|
|
if ( hasExt )
|
|
*hasExt = false;
|
|
}
|
|
else
|
|
{
|
|
// take everything after the dot
|
|
if ( pstrExt )
|
|
*pstrExt = fullpath.Mid(posLastDot + 1);
|
|
if ( hasExt )
|
|
*hasExt = true;
|
|
}
|
|
}
|
|
|
|
/* 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 )
|
|
{
|
|
path->Prepend(wxGetVolumeString(volume, format));
|
|
}
|
|
}
|
|
|
|
/* static */
|
|
wxString wxFileName::StripExtension(const wxString& fullpath)
|
|
{
|
|
wxFileName fn(fullpath);
|
|
fn.SetExt("");
|
|
return fn.GetFullPath();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// file permissions functions
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool wxFileName::SetPermissions(int permissions)
|
|
{
|
|
// Don't do anything for a symlink but first make sure it is one.
|
|
if ( m_dontFollowLinks &&
|
|
Exists(wxFILE_EXISTS_SYMLINK|wxFILE_EXISTS_NO_FOLLOW) )
|
|
{
|
|
// Looks like changing permissions for a symlinc is only supported
|
|
// on BSD where lchmod is present and correctly implemented.
|
|
// http://lists.gnu.org/archive/html/bug-coreutils/2009-09/msg00268.html
|
|
return false;
|
|
}
|
|
|
|
#ifdef __WINDOWS__
|
|
int accMode = 0;
|
|
|
|
if ( permissions & (wxS_IRUSR|wxS_IRGRP|wxS_IROTH) )
|
|
accMode = _S_IREAD;
|
|
|
|
if ( permissions & (wxS_IWUSR|wxS_IWGRP|wxS_IWOTH) )
|
|
accMode |= _S_IWRITE;
|
|
|
|
permissions = accMode;
|
|
#endif // __WINDOWS__
|
|
|
|
return wxChmod(GetFullPath(), permissions) == 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// time functions
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if wxUSE_DATETIME
|
|
|
|
bool wxFileName::SetTimes(const wxDateTime *dtAccess,
|
|
const wxDateTime *dtMod,
|
|
const wxDateTime *dtCreate) const
|
|
{
|
|
#if defined(__WIN32__)
|
|
FILETIME ftAccess, ftCreate, ftWrite;
|
|
|
|
if ( dtCreate )
|
|
ConvertWxToFileTime(&ftCreate, *dtCreate);
|
|
if ( dtAccess )
|
|
ConvertWxToFileTime(&ftAccess, *dtAccess);
|
|
if ( dtMod )
|
|
ConvertWxToFileTime(&ftWrite, *dtMod);
|
|
|
|
wxString path;
|
|
int flags;
|
|
if ( IsDir() )
|
|
{
|
|
if ( wxGetOsVersion() == wxOS_WINDOWS_9X )
|
|
{
|
|
wxLogError(_("Setting directory access times is not supported "
|
|
"under this OS version"));
|
|
return false;
|
|
}
|
|
|
|
path = GetPath();
|
|
flags = FILE_FLAG_BACKUP_SEMANTICS;
|
|
}
|
|
else // file
|
|
{
|
|
path = GetFullPath();
|
|
flags = 0;
|
|
}
|
|
|
|
wxFileHandle fh(path, wxFileHandle::WriteAttr, flags);
|
|
if ( fh.IsOk() )
|
|
{
|
|
if ( ::SetFileTime(fh,
|
|
dtCreate ? &ftCreate : NULL,
|
|
dtAccess ? &ftAccess : NULL,
|
|
dtMod ? &ftWrite : NULL) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
#elif defined(__UNIX_LIKE__)
|
|
wxUnusedVar(dtCreate);
|
|
|
|
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().fn_str(), &utm) == 0 )
|
|
{
|
|
return true;
|
|
}
|
|
#else // other platform
|
|
wxUnusedVar(dtAccess);
|
|
wxUnusedVar(dtMod);
|
|
wxUnusedVar(dtCreate);
|
|
#endif // platforms
|
|
|
|
wxLogSysError(_("Failed to modify file times for '%s'"),
|
|
GetFullPath().c_str());
|
|
|
|
return false;
|
|
}
|
|
|
|
bool wxFileName::Touch() const
|
|
{
|
|
#if defined(__UNIX_LIKE__)
|
|
// under Unix touching file is simple: just pass NULL to utime()
|
|
if ( utime(GetFullPath().fn_str(), 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(&dtNow, &dtNow, NULL /* don't change create time */);
|
|
#endif // platforms
|
|
}
|
|
|
|
bool wxFileName::GetTimes(wxDateTime *dtAccess,
|
|
wxDateTime *dtMod,
|
|
wxDateTime *dtCreate) const
|
|
{
|
|
#if defined(__WIN32__)
|
|
// we must use different methods for the files and directories under
|
|
// Windows as CreateFile(GENERIC_READ) doesn't work for the directories and
|
|
// CreateFile(FILE_FLAG_BACKUP_SEMANTICS) works -- but only under NT and
|
|
// not 9x
|
|
bool ok;
|
|
FILETIME ftAccess, ftCreate, ftWrite;
|
|
if ( IsDir() )
|
|
{
|
|
// implemented in msw/dir.cpp
|
|
extern bool wxGetDirectoryTimes(const wxString& dirname,
|
|
FILETIME *, FILETIME *, FILETIME *);
|
|
|
|
// we should pass the path without the trailing separator to
|
|
// wxGetDirectoryTimes()
|
|
ok = wxGetDirectoryTimes(GetPath(wxPATH_GET_VOLUME),
|
|
&ftAccess, &ftCreate, &ftWrite);
|
|
}
|
|
else // file
|
|
{
|
|
wxFileHandle fh(GetFullPath(), wxFileHandle::ReadAttr);
|
|
if ( fh.IsOk() )
|
|
{
|
|
ok = ::GetFileTime(fh,
|
|
dtCreate ? &ftCreate : NULL,
|
|
dtAccess ? &ftAccess : NULL,
|
|
dtMod ? &ftWrite : NULL) != 0;
|
|
}
|
|
else
|
|
{
|
|
ok = false;
|
|
}
|
|
}
|
|
|
|
if ( ok )
|
|
{
|
|
if ( dtCreate )
|
|
ConvertFileTimeToWx(dtCreate, ftCreate);
|
|
if ( dtAccess )
|
|
ConvertFileTimeToWx(dtAccess, ftAccess);
|
|
if ( dtMod )
|
|
ConvertFileTimeToWx(dtMod, ftWrite);
|
|
|
|
return true;
|
|
}
|
|
#elif defined(wxHAVE_LSTAT)
|
|
// no need to test for IsDir() here
|
|
wxStructStat stBuf;
|
|
if ( StatAny(stBuf, *this) )
|
|
{
|
|
// Android defines st_*time fields as unsigned long, but time_t as long,
|
|
// hence the static_casts.
|
|
if ( dtAccess )
|
|
dtAccess->Set(static_cast<time_t>(stBuf.st_atime));
|
|
if ( dtMod )
|
|
dtMod->Set(static_cast<time_t>(stBuf.st_mtime));
|
|
if ( dtCreate )
|
|
dtCreate->Set(static_cast<time_t>(stBuf.st_ctime));
|
|
|
|
return true;
|
|
}
|
|
#else // other platform
|
|
wxUnusedVar(dtAccess);
|
|
wxUnusedVar(dtMod);
|
|
wxUnusedVar(dtCreate);
|
|
#endif // platforms
|
|
|
|
wxLogSysError(_("Failed to retrieve file times for '%s'"),
|
|
GetFullPath().c_str());
|
|
|
|
return false;
|
|
}
|
|
|
|
#endif // wxUSE_DATETIME
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// file size functions
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if wxUSE_LONGLONG
|
|
|
|
/* static */
|
|
wxULongLong wxFileName::GetSize(const wxString &filename)
|
|
{
|
|
if (!wxFileExists(filename))
|
|
return wxInvalidSize;
|
|
|
|
#if defined(__WIN32__)
|
|
wxFileHandle f(filename, wxFileHandle::ReadAttr);
|
|
if (!f.IsOk())
|
|
return wxInvalidSize;
|
|
|
|
DWORD lpFileSizeHigh;
|
|
DWORD ret = GetFileSize(f, &lpFileSizeHigh);
|
|
if ( ret == INVALID_FILE_SIZE && ::GetLastError() != NO_ERROR )
|
|
return wxInvalidSize;
|
|
|
|
return wxULongLong(lpFileSizeHigh, ret);
|
|
#else // ! __WIN32__
|
|
wxStructStat st;
|
|
if (wxStat( filename, &st) != 0)
|
|
return wxInvalidSize;
|
|
return wxULongLong(st.st_size);
|
|
#endif
|
|
}
|
|
|
|
/* static */
|
|
wxString wxFileName::GetHumanReadableSize(const wxULongLong &bs,
|
|
const wxString &nullsize,
|
|
int precision,
|
|
wxSizeConvention conv)
|
|
{
|
|
// deal with trivial case first
|
|
if ( bs == 0 || bs == wxInvalidSize )
|
|
return nullsize;
|
|
|
|
// depending on the convention used the multiplier may be either 1000 or
|
|
// 1024 and the binary infix may be empty (for "KB") or "i" (for "KiB")
|
|
double multiplier = 1024.;
|
|
wxString biInfix;
|
|
|
|
switch ( conv )
|
|
{
|
|
case wxSIZE_CONV_TRADITIONAL:
|
|
// nothing to do, this corresponds to the default values of both
|
|
// the multiplier and infix string
|
|
break;
|
|
|
|
case wxSIZE_CONV_IEC:
|
|
biInfix = "i";
|
|
break;
|
|
|
|
case wxSIZE_CONV_SI:
|
|
multiplier = 1000;
|
|
break;
|
|
}
|
|
|
|
const double kiloByteSize = multiplier;
|
|
const double megaByteSize = multiplier * kiloByteSize;
|
|
const double gigaByteSize = multiplier * megaByteSize;
|
|
const double teraByteSize = multiplier * gigaByteSize;
|
|
|
|
const double bytesize = bs.ToDouble();
|
|
|
|
wxString result;
|
|
if ( bytesize < kiloByteSize )
|
|
result.Printf("%s B", bs.ToString());
|
|
else if ( bytesize < megaByteSize )
|
|
result.Printf("%.*f K%sB", precision, bytesize/kiloByteSize, biInfix);
|
|
else if (bytesize < gigaByteSize)
|
|
result.Printf("%.*f M%sB", precision, bytesize/megaByteSize, biInfix);
|
|
else if (bytesize < teraByteSize)
|
|
result.Printf("%.*f G%sB", precision, bytesize/gigaByteSize, biInfix);
|
|
else
|
|
result.Printf("%.*f T%sB", precision, bytesize/teraByteSize, biInfix);
|
|
|
|
return result;
|
|
}
|
|
|
|
wxULongLong wxFileName::GetSize() const
|
|
{
|
|
return GetSize(GetFullPath());
|
|
}
|
|
|
|
wxString wxFileName::GetHumanReadableSize(const wxString& failmsg,
|
|
int precision,
|
|
wxSizeConvention conv) const
|
|
{
|
|
return GetHumanReadableSize(GetSize(), failmsg, precision, conv);
|
|
}
|
|
|
|
#endif // wxUSE_LONGLONG
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Mac-specific functions
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if defined( __WXOSX_MAC__ ) && wxOSX_USE_CARBON
|
|
|
|
namespace
|
|
{
|
|
|
|
class MacDefaultExtensionRecord
|
|
{
|
|
public:
|
|
MacDefaultExtensionRecord()
|
|
{
|
|
m_type =
|
|
m_creator = 0 ;
|
|
}
|
|
|
|
// default copy ctor, assignment operator and dtor are ok
|
|
|
|
MacDefaultExtensionRecord(const wxString& ext, OSType type, OSType creator)
|
|
: m_ext(ext)
|
|
{
|
|
m_type = type;
|
|
m_creator = creator;
|
|
}
|
|
|
|
wxString m_ext;
|
|
OSType m_type;
|
|
OSType m_creator;
|
|
};
|
|
|
|
WX_DECLARE_OBJARRAY(MacDefaultExtensionRecord, MacDefaultExtensionArray);
|
|
|
|
bool gMacDefaultExtensionsInited = false;
|
|
|
|
#include "wx/arrimpl.cpp"
|
|
|
|
WX_DEFINE_EXPORTED_OBJARRAY(MacDefaultExtensionArray);
|
|
|
|
MacDefaultExtensionArray gMacDefaultExtensions;
|
|
|
|
// load the default extensions
|
|
const MacDefaultExtensionRecord gDefaults[] =
|
|
{
|
|
MacDefaultExtensionRecord( "txt", 'TEXT', 'ttxt' ),
|
|
MacDefaultExtensionRecord( "tif", 'TIFF', '****' ),
|
|
MacDefaultExtensionRecord( "jpg", 'JPEG', '****' ),
|
|
};
|
|
|
|
void MacEnsureDefaultExtensionsLoaded()
|
|
{
|
|
if ( !gMacDefaultExtensionsInited )
|
|
{
|
|
// we could load the pc exchange prefs here too
|
|
for ( size_t i = 0 ; i < WXSIZEOF( gDefaults ) ; ++i )
|
|
{
|
|
gMacDefaultExtensions.Add( gDefaults[i] ) ;
|
|
}
|
|
gMacDefaultExtensionsInited = true;
|
|
}
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
bool wxFileName::MacSetTypeAndCreator( wxUint32 type , wxUint32 creator )
|
|
{
|
|
FSRef fsRef ;
|
|
FSCatalogInfo catInfo;
|
|
FileInfo *finfo ;
|
|
|
|
if ( wxMacPathToFSRef( GetFullPath() , &fsRef ) == noErr )
|
|
{
|
|
if ( FSGetCatalogInfo (&fsRef, kFSCatInfoFinderInfo, &catInfo, NULL, NULL, NULL) == noErr )
|
|
{
|
|
finfo = (FileInfo*)&catInfo.finderInfo;
|
|
finfo->fileType = type ;
|
|
finfo->fileCreator = creator ;
|
|
FSSetCatalogInfo( &fsRef, kFSCatInfoFinderInfo, &catInfo ) ;
|
|
return true ;
|
|
}
|
|
}
|
|
return false ;
|
|
}
|
|
|
|
bool wxFileName::MacGetTypeAndCreator( wxUint32 *type , wxUint32 *creator ) const
|
|
{
|
|
FSRef fsRef ;
|
|
FSCatalogInfo catInfo;
|
|
FileInfo *finfo ;
|
|
|
|
if ( wxMacPathToFSRef( GetFullPath() , &fsRef ) == noErr )
|
|
{
|
|
if ( FSGetCatalogInfo (&fsRef, kFSCatInfoFinderInfo, &catInfo, NULL, NULL, NULL) == noErr )
|
|
{
|
|
finfo = (FileInfo*)&catInfo.finderInfo;
|
|
*type = finfo->fileType ;
|
|
*creator = finfo->fileCreator ;
|
|
return true ;
|
|
}
|
|
}
|
|
return false ;
|
|
}
|
|
|
|
bool wxFileName::MacSetDefaultTypeAndCreator()
|
|
{
|
|
wxUint32 type , creator ;
|
|
if ( wxFileName::MacFindDefaultTypeAndCreator(GetExt() , &type ,
|
|
&creator ) )
|
|
{
|
|
return MacSetTypeAndCreator( type , creator ) ;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool wxFileName::MacFindDefaultTypeAndCreator( const wxString& ext , wxUint32 *type , wxUint32 *creator )
|
|
{
|
|
MacEnsureDefaultExtensionsLoaded() ;
|
|
wxString extl = ext.Lower() ;
|
|
for( int i = gMacDefaultExtensions.Count() - 1 ; i >= 0 ; --i )
|
|
{
|
|
if ( gMacDefaultExtensions.Item(i).m_ext == extl )
|
|
{
|
|
*type = gMacDefaultExtensions.Item(i).m_type ;
|
|
*creator = gMacDefaultExtensions.Item(i).m_creator ;
|
|
return true ;
|
|
}
|
|
}
|
|
return false ;
|
|
}
|
|
|
|
void wxFileName::MacRegisterDefaultTypeAndCreator( const wxString& ext , wxUint32 type , wxUint32 creator )
|
|
{
|
|
MacEnsureDefaultExtensionsLoaded();
|
|
MacDefaultExtensionRecord rec(ext.Lower(), type, creator);
|
|
gMacDefaultExtensions.Add( rec );
|
|
}
|
|
|
|
#endif // defined( __WXOSX_MAC__ ) && wxOSX_USE_CARBON
|