Add a new wxFileName function to resolve symlinks to absolute paths
This commit is contained in:
@@ -381,7 +381,10 @@ enum wxPosixPermissions
|
||||
#define wxCRT_Access access
|
||||
#define wxCRT_Chmod chmod
|
||||
|
||||
#define wxCRT_Readlink readlink
|
||||
|
||||
#define wxHAS_NATIVE_LSTAT
|
||||
#define wxHAS_NATIVE_READLINK
|
||||
#endif // platforms
|
||||
|
||||
// if the platform doesn't have symlinks, define wxCRT_Lstat to be the same as
|
||||
@@ -406,6 +409,11 @@ inline int wxChmod(const wxString& path, mode_t mode)
|
||||
inline int wxOpen(const wxString& path, int flags, mode_t mode)
|
||||
{ return wxCRT_Open(path.fn_str(), flags, mode); }
|
||||
|
||||
#if defined(wxHAS_NATIVE_READLINK)
|
||||
inline int wxReadlink(const wxString& path, char* buf, int size)
|
||||
{ return wxCRT_Readlink(path.fn_str(), buf, size); }
|
||||
#endif
|
||||
|
||||
inline int wxStat(const wxString& path, wxStructStat *buf)
|
||||
{ return wxCRT_Stat(path.fn_str(), buf); }
|
||||
inline int wxLstat(const wxString& path, wxStructStat *buf)
|
||||
|
@@ -389,6 +389,9 @@ public:
|
||||
return !m_dontFollowLinks;
|
||||
}
|
||||
|
||||
// Resolve a wxFileName object representing a link to its target
|
||||
wxFileName ResolveLink();
|
||||
|
||||
#if defined(__WIN32__) && wxUSE_OLE
|
||||
// if the path is a shortcut, return the target and optionally,
|
||||
// the arguments
|
||||
|
@@ -1138,6 +1138,24 @@ public:
|
||||
*/
|
||||
bool ReplaceHomeDir(wxPathFormat format = wxPATH_NATIVE);
|
||||
|
||||
/**
|
||||
Find the absolute path of the file/directory that is pointed to by this
|
||||
path.
|
||||
|
||||
If this path isn't a symlink, then this function will return the current
|
||||
path. If the path does not exist on disk, An empty wxFileName instance
|
||||
will be returned.
|
||||
|
||||
@note This is only supported on Unix-like platforms (e.g. wxGTK, wxOSX),
|
||||
on other platforms (e.g. wxMSW) this function just returns the
|
||||
current path.
|
||||
|
||||
@since 3.1.5
|
||||
|
||||
@return The absolute path that the current symlink path points to.
|
||||
*/
|
||||
wxFileName ResolveLink();
|
||||
|
||||
|
||||
/**
|
||||
Deletes the specified directory from the file system.
|
||||
|
@@ -1668,6 +1668,56 @@ bool wxFileName::GetShortcutTarget(const wxString& shortcutPath,
|
||||
|
||||
#endif // __WIN32__
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Resolve links
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
wxFileName wxFileName::ResolveLink()
|
||||
{
|
||||
wxString link = GetFullPath();
|
||||
wxFileName linkTarget( *this );
|
||||
|
||||
// Only resolve links on platforms with readlink (e.g. Unix-like platforms)
|
||||
#if defined(wxHAS_NATIVE_READLINK)
|
||||
wxStructStat st;
|
||||
|
||||
// This means the link itself doesn't exist, so return an empty filename
|
||||
if ( !StatAny(st, link, wxFILE_EXISTS_NO_FOLLOW) )
|
||||
{
|
||||
linkTarget.Clear();
|
||||
return linkTarget;
|
||||
}
|
||||
|
||||
// If it isn't an actual link, bail out just and return the link as the result
|
||||
if ( !S_ISLNK(st.st_mode) )
|
||||
return linkTarget;
|
||||
|
||||
// Dynamically compute the buffer size from the stat call, but fallback if it isn't valid
|
||||
int bufSize = 4096;
|
||||
if( st.st_size != 0 )
|
||||
bufSize = st.st_size + 1;
|
||||
|
||||
char buf[bufSize];
|
||||
int result = wxReadlink(link, buf, bufSize - 1);
|
||||
|
||||
if ( result != -1 )
|
||||
{
|
||||
buf[result] = '\0'; // readlink() doesn't NULL-terminate the buffer
|
||||
linkTarget.Assign( wxString(buf, wxConvLibc) );
|
||||
|
||||
// Ensure the resulting path is absolute since readlink can return paths relative to the link
|
||||
if ( !linkTarget.IsAbsolute() )
|
||||
linkTarget.MakeAbsolute(GetPath());
|
||||
}
|
||||
else
|
||||
{
|
||||
// This means the lookup failed for some reason
|
||||
linkTarget.Clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
return linkTarget;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// absolute/relative paths
|
||||
|
@@ -842,25 +842,71 @@ void FileNameTestCase::TestSymlinks()
|
||||
wxFileName targetfn(wxFileName::CreateTempFileName(tempdir));
|
||||
CPPUNIT_ASSERT(targetfn.FileExists());
|
||||
|
||||
// Resolving a non-symlink will just return the same thing
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE
|
||||
(
|
||||
"Non-symlink didn't resolve to the same file",
|
||||
targetfn, targetfn.ResolveLink()
|
||||
);
|
||||
|
||||
// Create a symlink to that file
|
||||
wxFileName linktofile(tempdir, "linktofile");
|
||||
CPPUNIT_ASSERT_EQUAL(0, symlink(targetfn.GetFullPath().c_str(),
|
||||
linktofile.GetFullPath().c_str()));
|
||||
linktofile.GetFullPath().c_str()));
|
||||
|
||||
// Test resolving the filename to the symlink
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE
|
||||
(
|
||||
"Failed to resolve symlink to file",
|
||||
targetfn, linktofile.ResolveLink()
|
||||
);
|
||||
|
||||
// Create a relative symlink to that file
|
||||
wxFileName relativelinktofile(tempdir, "relativelinktofile");
|
||||
wxString relativeFileName = "./" + targetfn.GetFullName();
|
||||
CPPUNIT_ASSERT_EQUAL(0, symlink(relativeFileName.c_str(),
|
||||
relativelinktofile.GetFullPath().c_str()));
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE
|
||||
(
|
||||
"Failed to resolve relative symlink to absolute file path",
|
||||
targetfn, relativelinktofile.ResolveLink()
|
||||
);
|
||||
|
||||
// ... and another to the temporary directory
|
||||
const wxString linktodirName(tempdir + "/linktodir");
|
||||
wxFileName linktodirfn(linktodirName);
|
||||
wxFileName linktodir(wxFileName::DirName(linktodirName));
|
||||
CPPUNIT_ASSERT_EQUAL(0, symlink(tmpfn.GetFullPath().c_str(),
|
||||
linktodirName.c_str()));
|
||||
linktodirName.c_str()));
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE
|
||||
(
|
||||
"Failed to resolve symlink to directory",
|
||||
tmpfn, linktodirfn.ResolveLink()
|
||||
);
|
||||
|
||||
// And symlinks to both of those symlinks
|
||||
wxFileName linktofilelnk(tempdir, "linktofilelnk");
|
||||
CPPUNIT_ASSERT_EQUAL(0, symlink(linktofile.GetFullPath().c_str(),
|
||||
linktofilelnk.GetFullPath().c_str()));
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE
|
||||
(
|
||||
"Failed to resolve symlink to file symlink",
|
||||
targetfn, linktofilelnk.ResolveLink()
|
||||
);
|
||||
|
||||
wxFileName linktodirlnk(tempdir, "linktodirlnk");
|
||||
CPPUNIT_ASSERT_EQUAL(0, symlink(linktodir.GetFullPath().c_str(),
|
||||
linktodirlnk.GetFullPath().c_str()));
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE
|
||||
(
|
||||
"Failed to resolve symlink to directory symlink",
|
||||
tmpfn, linktodirlnk.ResolveLink()
|
||||
);
|
||||
|
||||
// Run the tests twice: once in the default symlink following mode and the
|
||||
// second time without following symlinks.
|
||||
bool deref = true;
|
||||
@@ -969,6 +1015,14 @@ void FileNameTestCase::TestSymlinks()
|
||||
|
||||
// Finally test Exists() after removing the file.
|
||||
CPPUNIT_ASSERT(wxRemoveFile(targetfn.GetFullPath()));
|
||||
|
||||
// Resolving a file that doesn't exist returns empty
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE
|
||||
(
|
||||
"Non-existant file didn't resolve correctly",
|
||||
wxFileName(), targetfn.ResolveLink()
|
||||
);
|
||||
|
||||
// This should succeed, as the symlink still exists and
|
||||
// the default wxFILE_EXISTS_ANY implies wxFILE_EXISTS_NO_FOLLOW
|
||||
CPPUNIT_ASSERT(wxFileName(tempdir, "linktofile").Exists());
|
||||
|
Reference in New Issue
Block a user