Author: labath
Date: Mon Apr  4 09:39:12 2016
New Revision: 265299

URL: http://llvm.org/viewvc/llvm-project?rev=265299&view=rev
Log:
Make FileSpec handling platform-independent

Summary:
Even though FileSpec attempted to handle both kinds of path syntaxes (posix and 
windows) on both
platforms, it relied on the llvm path library to do its work, whose behavior 
differed on
different platforms. This led to subtle differences in FileSpec behavior 
between platforms. This
replaces the pieces of the llvm library with our own implementations. The 
functions are simply
copied from llvm, with #ifdefs replaced by runtime checks for 
ePathSyntaxWindows.

Reviewers: zturner

Subscribers: lldb-commits

Differential Revision: http://reviews.llvm.org/D18689

Modified:
    lldb/trunk/source/Host/common/FileSpec.cpp
    lldb/trunk/unittests/Host/FileSpecTest.cpp

Modified: lldb/trunk/source/Host/common/FileSpec.cpp
URL: 
http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Host/common/FileSpec.cpp?rev=265299&r1=265298&r2=265299&view=diff
==============================================================================
--- lldb/trunk/source/Host/common/FileSpec.cpp (original)
+++ lldb/trunk/source/Host/common/FileSpec.cpp Mon Apr  4 09:39:12 2016
@@ -57,10 +57,22 @@ PathSyntaxIsPosix(FileSpec::PathSyntax s
              FileSystem::GetNativePathSyntax() == FileSpec::ePathSyntaxPosix));
 }
 
+const char *
+GetPathSeparators(FileSpec::PathSyntax syntax)
+{
+    return PathSyntaxIsPosix(syntax) ? "/" : "\\/";
+}
+
 char
-GetPathSeparator(FileSpec::PathSyntax syntax)
+GetPrefferedPathSeparator(FileSpec::PathSyntax syntax)
+{
+    return GetPathSeparators(syntax)[0];
+}
+
+bool
+IsPathSeparator(char value, FileSpec::PathSyntax syntax)
 {
-    return PathSyntaxIsPosix(syntax) ? '/' : '\\';
+    return value == '/' || (!PathSyntaxIsPosix(syntax) && value == '\\');
 }
 
 void
@@ -94,8 +106,69 @@ GetFileStats (const FileSpec *file_spec,
     return false;
 }
 
+size_t
+FilenamePos(llvm::StringRef str, FileSpec::PathSyntax syntax)
+{
+    if (str.size() == 2 && IsPathSeparator(str[0], syntax) && str[0] == str[1])
+        return 0;
+
+    if (str.size() > 0 && IsPathSeparator(str.back(), syntax))
+        return str.size() - 1;
+
+    size_t pos = str.find_last_of(GetPathSeparators(syntax), str.size() - 1);
+
+    if (!PathSyntaxIsPosix(syntax) && pos == llvm::StringRef::npos)
+        pos = str.find_last_of(':', str.size() - 2);
+
+    if (pos == llvm::StringRef::npos || (pos == 1 && IsPathSeparator(str[0], 
syntax)))
+        return 0;
+
+    return pos + 1;
 }
 
+size_t
+RootDirStart(llvm::StringRef str, FileSpec::PathSyntax syntax)
+{
+    // case "c:/"
+    if (!PathSyntaxIsPosix(syntax) && (str.size() > 2 && str[1] == ':' && 
IsPathSeparator(str[2], syntax)))
+        return 2;
+
+    // case "//"
+    if (str.size() == 2 && IsPathSeparator(str[0], syntax) && str[0] == str[1])
+        return llvm::StringRef::npos;
+
+    // case "//net"
+    if (str.size() > 3 && IsPathSeparator(str[0], syntax) && str[0] == str[1] 
&& !IsPathSeparator(str[2], syntax))
+        return str.find_first_of(GetPathSeparators(syntax), 2);
+
+    // case "/"
+    if (str.size() > 0 && IsPathSeparator(str[0], syntax))
+        return 0;
+
+    return llvm::StringRef::npos;
+}
+
+size_t
+ParentPathEnd(llvm::StringRef path, FileSpec::PathSyntax syntax)
+{
+    size_t end_pos = FilenamePos(path, syntax);
+
+    bool filename_was_sep = path.size() > 0 && IsPathSeparator(path[end_pos], 
syntax);
+
+    // Skip separators except for root dir.
+    size_t root_dir_pos = RootDirStart(path.substr(0, end_pos), syntax);
+
+    while (end_pos > 0 && (end_pos - 1) != root_dir_pos && 
IsPathSeparator(path[end_pos - 1], syntax))
+        --end_pos;
+
+    if (end_pos == 1 && root_dir_pos == 0 && filename_was_sep)
+        return llvm::StringRef::npos;
+
+    return end_pos;
+}
+
+} // end anonymous namespace
+
 // Resolves the username part of a path of the form ~user/other/directories, 
and
 // writes the result into dst_path.  This will also resolve "~" to the current 
user.
 // If you want to complete "~" to the list of users, pass it to 
ResolvePartialUsername.
@@ -313,30 +386,32 @@ FileSpec::SetFile (const char *pathname,
     if (pathname == NULL || pathname[0] == '\0')
         return;
 
-    llvm::SmallString<64> normalized(pathname);
+    llvm::SmallString<64> resolved(pathname);
 
     if (resolve)
     {
-        FileSpec::Resolve (normalized);
+        FileSpec::Resolve (resolved);
         m_is_resolved = true;
     }
 
-    // Only normalize after resolving the path.  Resolution will modify the 
path
-    // string, potentially adding wrong kinds of slashes to the path, that need
-    // to be re-normalized.
-    Normalize(normalized, syntax);
-
-    llvm::StringRef resolve_path_ref(normalized.c_str());
-    llvm::StringRef filename_ref = llvm::sys::path::filename(resolve_path_ref);
-    if (!filename_ref.empty())
-    {
-        m_filename.SetString (filename_ref);
-        llvm::StringRef directory_ref = 
llvm::sys::path::parent_path(resolve_path_ref);
-        if (!directory_ref.empty())
-            m_directory.SetString(directory_ref);
+    llvm::StringRef resolve_path_ref(resolved.c_str());
+    size_t dir_end = ParentPathEnd(resolve_path_ref, syntax);
+    if (dir_end == 0)
+    {
+        m_filename.SetString(resolve_path_ref);
+        return;
     }
-    else
-        m_directory.SetCString(normalized.c_str());
+
+    m_directory.SetString(resolve_path_ref.substr(0, dir_end));
+
+    size_t filename_begin = dir_end;
+    size_t root_dir_start = RootDirStart(resolve_path_ref, syntax);
+    while (filename_begin != llvm::StringRef::npos && filename_begin < 
resolve_path_ref.size() &&
+           filename_begin != root_dir_start && 
IsPathSeparator(resolve_path_ref[filename_begin], syntax))
+        ++filename_begin;
+    m_filename.SetString((filename_begin == llvm::StringRef::npos || 
filename_begin >= resolve_path_ref.size())
+                             ? "."
+                             : resolve_path_ref.substr(filename_begin));
 }
 
 void
@@ -683,7 +758,7 @@ FileSpec::Dump(Stream *s) const
     {
         std::string path{GetPath(true)};
         s->PutCString(path.c_str());
-        char path_separator = GetPathSeparator(m_syntax);
+        char path_separator = GetPrefferedPathSeparator(m_syntax);
         if (!m_filename && !path.empty() && path.back() != path_separator)
             s->PutChar(path_separator);
     }
@@ -916,11 +991,10 @@ void
 FileSpec::GetPath(llvm::SmallVectorImpl<char> &path, bool denormalize) const
 {
     path.append(m_directory.GetStringRef().begin(), 
m_directory.GetStringRef().end());
-    if (m_directory && !(m_directory.GetLength() == 1 && 
m_directory.GetCString()[0] == '/'))
-        path.insert(path.end(), '/');
+    if (m_directory && m_filename && 
!IsPathSeparator(m_directory.GetStringRef().back(), m_syntax))
+        path.insert(path.end(), GetPrefferedPathSeparator(m_syntax));
     path.append(m_filename.GetStringRef().begin(), 
m_filename.GetStringRef().end());
     Normalize(path, m_syntax);
-    if (path.size() > 1 && path.back() == '/') path.pop_back();
     if (denormalize && !path.empty())
         Denormalize(path, m_syntax);
 }
@@ -1451,15 +1525,15 @@ FileSpec::AppendPathComponent(const char
     if (!m_directory.IsEmpty())
     {
         stream.PutCString(m_directory.GetCString());
-        if (m_directory.GetLength() != 1 || m_directory.GetCString()[0] != '/')
-            stream.PutChar('/');
+        if (!IsPathSeparator(m_directory.GetStringRef().back(), m_syntax))
+            stream.PutChar(GetPrefferedPathSeparator(m_syntax));
     }
 
     if (!m_filename.IsEmpty())
     {
         stream.PutCString(m_filename.GetCString());
-        if (m_filename.GetLength() != 1 || m_filename.GetCString()[0] != '/')
-            stream.PutChar('/');
+        if (!IsPathSeparator(m_filename.GetStringRef().back(), m_syntax))
+            stream.PutChar(GetPrefferedPathSeparator(m_syntax));
     }
 
     stream.PutCString(new_path);

Modified: lldb/trunk/unittests/Host/FileSpecTest.cpp
URL: 
http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Host/FileSpecTest.cpp?rev=265299&r1=265298&r2=265299&view=diff
==============================================================================
--- lldb/trunk/unittests/Host/FileSpecTest.cpp (original)
+++ lldb/trunk/unittests/Host/FileSpecTest.cpp Mon Apr  4 09:39:12 2016
@@ -22,7 +22,7 @@ TEST(FileSpecTest, FileAndDirectoryCompo
 
     FileSpec fs_windows("F:\\bar", false, FileSpec::ePathSyntaxWindows);
     EXPECT_STREQ("F:\\bar", fs_windows.GetCString());
-    EXPECT_STREQ("F:", fs_windows.GetDirectory().GetCString());
+    EXPECT_STREQ("F:\\", fs_windows.GetDirectory().GetCString());
     EXPECT_STREQ("bar", fs_windows.GetFilename().GetCString());
 
     FileSpec fs_posix_root("/", false, FileSpec::ePathSyntaxPosix);
@@ -30,10 +30,15 @@ TEST(FileSpecTest, FileAndDirectoryCompo
     EXPECT_EQ(nullptr, fs_posix_root.GetDirectory().GetCString());
     EXPECT_STREQ("/", fs_posix_root.GetFilename().GetCString());
 
-    FileSpec fs_windows_root("F:", false, FileSpec::ePathSyntaxWindows);
-    EXPECT_STREQ("F:", fs_windows_root.GetCString());
-    EXPECT_EQ(nullptr, fs_windows_root.GetDirectory().GetCString());
-    EXPECT_STREQ("F:", fs_windows_root.GetFilename().GetCString());
+    FileSpec fs_windows_drive("F:", false, FileSpec::ePathSyntaxWindows);
+    EXPECT_STREQ("F:", fs_windows_drive.GetCString());
+    EXPECT_EQ(nullptr, fs_windows_drive.GetDirectory().GetCString());
+    EXPECT_STREQ("F:", fs_windows_drive.GetFilename().GetCString());
+
+    FileSpec fs_windows_root("F:\\", false, FileSpec::ePathSyntaxWindows);
+    EXPECT_STREQ("F:\\", fs_windows_root.GetCString());
+    EXPECT_STREQ("F:", fs_windows_root.GetDirectory().GetCString());
+    EXPECT_STREQ("\\", fs_windows_root.GetFilename().GetCString());
 
     FileSpec fs_posix_long("/foo/bar/baz", false, FileSpec::ePathSyntaxPosix);
     EXPECT_STREQ("/foo/bar/baz", fs_posix_long.GetCString());
@@ -43,7 +48,7 @@ TEST(FileSpecTest, FileAndDirectoryCompo
     FileSpec fs_windows_long("F:\\bar\\baz", false, 
FileSpec::ePathSyntaxWindows);
     EXPECT_STREQ("F:\\bar\\baz", fs_windows_long.GetCString());
     // We get "F:/bar" instead.
-    // EXPECT_STREQ("F:\\bar", fs_windows_long.GetDirectory().GetCString());
+    EXPECT_STREQ("F:\\bar", fs_windows_long.GetDirectory().GetCString());
     EXPECT_STREQ("baz", fs_windows_long.GetFilename().GetCString());
 
     FileSpec fs_posix_trailing_slash("/foo/bar/", false, 
FileSpec::ePathSyntaxPosix);
@@ -54,7 +59,7 @@ TEST(FileSpecTest, FileAndDirectoryCompo
     FileSpec fs_windows_trailing_slash("F:\\bar\\", false, 
FileSpec::ePathSyntaxWindows);
     EXPECT_STREQ("F:\\bar\\.", fs_windows_trailing_slash.GetCString());
     // We get "F:/bar" instead.
-    // EXPECT_STREQ("F:\\bar", 
fs_windows_trailing_slash.GetDirectory().GetCString());
+    EXPECT_STREQ("F:\\bar", 
fs_windows_trailing_slash.GetDirectory().GetCString());
     EXPECT_STREQ(".", fs_windows_trailing_slash.GetFilename().GetCString());
 }
 
@@ -66,11 +71,11 @@ TEST(FileSpecTest, AppendPathComponent)
     EXPECT_STREQ("/foo", fs_posix.GetDirectory().GetCString());
     EXPECT_STREQ("bar", fs_posix.GetFilename().GetCString());
 
-    FileSpec fs_windows("F:", false, FileSpec::ePathSyntaxWindows);
-    fs_windows.AppendPathComponent("bar");
-    EXPECT_STREQ("F:\\bar", fs_windows.GetCString());
-    EXPECT_STREQ("F:", fs_windows.GetDirectory().GetCString());
-    EXPECT_STREQ("bar", fs_windows.GetFilename().GetCString());
+    FileSpec fs_windows("F:\\bar", false, FileSpec::ePathSyntaxWindows);
+    fs_windows.AppendPathComponent("baz");
+    EXPECT_STREQ("F:\\bar\\baz", fs_windows.GetCString());
+    EXPECT_STREQ("F:\\bar", fs_windows.GetDirectory().GetCString());
+    EXPECT_STREQ("baz", fs_windows.GetFilename().GetCString());
 
     FileSpec fs_posix_root("/", false, FileSpec::ePathSyntaxPosix);
     fs_posix_root.AppendPathComponent("bar");
@@ -78,10 +83,10 @@ TEST(FileSpecTest, AppendPathComponent)
     EXPECT_STREQ("/", fs_posix_root.GetDirectory().GetCString());
     EXPECT_STREQ("bar", fs_posix_root.GetFilename().GetCString());
 
-    FileSpec fs_windows_root("F:", false, FileSpec::ePathSyntaxWindows);
+    FileSpec fs_windows_root("F:\\", false, FileSpec::ePathSyntaxWindows);
     fs_windows_root.AppendPathComponent("bar");
     EXPECT_STREQ("F:\\bar", fs_windows_root.GetCString());
-    EXPECT_STREQ("F:", fs_windows_root.GetDirectory().GetCString());
+    EXPECT_STREQ("F:\\", fs_windows_root.GetDirectory().GetCString());
     EXPECT_STREQ("bar", fs_windows_root.GetFilename().GetCString());
 }
 


_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to