Index: lldb/include/lldb/Host/FileSpec.h =================================================================== --- lldb/include/lldb/Host/FileSpec.h +++ lldb/include/lldb/Host/FileSpec.h @@ -17,7 +17,6 @@ // Other libraries and framework includes // Project includes -#include "lldb/Host/PosixApi.h" #include "lldb/Utility/ConstString.h" #include "lldb/lldb-private.h" @@ -539,26 +538,6 @@ ConstString GetLastPathComponent() const; - //------------------------------------------------------------------ - /// Resolves the user name at the beginning of \a src_path, and writes the - /// output - /// to \a dst_path. Note, \a src_path can contain other path components after - /// the - /// user name, they will be copied over, and if the path doesn't start with - /// "~" it - /// will also be copied over to \a dst_path. - /// - /// @param[in] src_path - /// Input path to be resolved. - /// - /// @param[in] dst_path - /// Buffer to store the resolved path. - //------------------------------------------------------------------ - static void ResolveUsername(llvm::SmallVectorImpl &path); - - static size_t ResolvePartialUsername(llvm::StringRef partial_name, - StringList &matches); - enum EnumerateDirectoryResult { eEnumerateDirectoryResultNext, // Enumerate next entry in the current // directory Index: lldb/include/lldb/Utility/TildeExpressionResolver.h =================================================================== --- lldb/include/lldb/Utility/TildeExpressionResolver.h +++ lldb/include/lldb/Utility/TildeExpressionResolver.h @@ -44,6 +44,11 @@ /// \returns true if there were any matches, false otherwise. virtual bool ResolvePartial(llvm::StringRef Expr, llvm::StringSet<> &Output) = 0; + + /// \brief Resolve an entire path that begins with a tilde expression, + /// replacing the username portion with the matched result. + bool ResolveFullPath(llvm::StringRef Expr, + llvm::SmallVectorImpl &Output); }; class StandardTildeExpressionResolver : public TildeExpressionResolver { Index: lldb/source/API/SBDeclaration.cpp =================================================================== --- lldb/source/API/SBDeclaration.cpp +++ lldb/source/API/SBDeclaration.cpp @@ -1,5 +1,4 @@ -//===-- SBDeclaration.cpp -----------------------------------------*- C++ -//-*-===// +//===-- SBDeclaration.cpp ----------------------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // @@ -10,6 +9,7 @@ #include "lldb/API/SBDeclaration.h" #include "lldb/API/SBStream.h" +#include "lldb/Host/PosixApi.h" #include "lldb/Symbol/Declaration.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Stream.h" Index: lldb/source/API/SBFileSpec.cpp =================================================================== --- lldb/source/API/SBFileSpec.cpp +++ lldb/source/API/SBFileSpec.cpp @@ -13,6 +13,7 @@ #include "lldb/API/SBFileSpec.h" #include "lldb/API/SBStream.h" #include "lldb/Host/FileSpec.h" +#include "lldb/Host/PosixApi.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Stream.h" Index: lldb/source/API/SBFileSpecList.cpp =================================================================== --- lldb/source/API/SBFileSpecList.cpp +++ lldb/source/API/SBFileSpecList.cpp @@ -14,6 +14,7 @@ #include "lldb/API/SBStream.h" #include "lldb/Core/FileSpecList.h" #include "lldb/Host/FileSpec.h" +#include "lldb/Host/PosixApi.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Stream.h" Index: lldb/source/API/SBLineEntry.cpp =================================================================== --- lldb/source/API/SBLineEntry.cpp +++ lldb/source/API/SBLineEntry.cpp @@ -11,6 +11,7 @@ #include "lldb/API/SBLineEntry.h" #include "lldb/API/SBStream.h" +#include "lldb/Host/PosixApi.h" #include "lldb/Symbol/LineEntry.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/StreamString.h" Index: lldb/source/Host/common/FileSpec.cpp =================================================================== --- lldb/source/Host/common/FileSpec.cpp +++ lldb/source/Host/common/FileSpec.cpp @@ -7,21 +7,12 @@ // //===----------------------------------------------------------------------===// -#include -#include -#include - -#include "llvm/Config/llvm-config.h" -#ifndef LLVM_ON_WIN32 -#include -#endif - -#include "lldb/Core/StringList.h" #include "lldb/Host/FileSpec.h" #include "lldb/Utility/CleanUp.h" #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StreamString.h" +#include "lldb/Utility/TildeExpressionResolver.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" @@ -144,115 +135,13 @@ } // 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. -void FileSpec::ResolveUsername(llvm::SmallVectorImpl &path) { -#ifndef LLVM_ON_WIN32 - if (path.empty() || path[0] != '~') - return; - - llvm::StringRef path_str(path.data(), path.size()); - size_t slash_pos = path_str.find('/', 1); - if (slash_pos == 1 || path.size() == 1) { - // A path of ~/ resolves to the current user's home dir - llvm::SmallString<64> home_dir; - // llvm::sys::path::home_directory() only checks if "HOME" is set in the - // environment and does nothing else to locate the user home directory - if (!llvm::sys::path::home_directory(home_dir)) { - struct passwd *pw = getpwuid(getuid()); - if (pw && pw->pw_dir && pw->pw_dir[0]) { - // Update our environemnt so llvm::sys::path::home_directory() works - // next time - setenv("HOME", pw->pw_dir, 0); - home_dir.assign(llvm::StringRef(pw->pw_dir)); - } else { - return; - } - } - - // Overwrite the ~ with the first character of the homedir, and insert - // the rest. This way we only trigger one move, whereas an insert - // followed by a delete (or vice versa) would trigger two. - path[0] = home_dir[0]; - path.insert(path.begin() + 1, home_dir.begin() + 1, home_dir.end()); - return; - } - - auto username_begin = path.begin() + 1; - auto username_end = (slash_pos == llvm::StringRef::npos) - ? path.end() - : (path.begin() + slash_pos); - size_t replacement_length = std::distance(path.begin(), username_end); - - llvm::SmallString<20> username(username_begin, username_end); - struct passwd *user_entry = ::getpwnam(username.c_str()); - if (user_entry != nullptr) { - // Copy over the first n characters of the path, where n is the smaller of - // the length - // of the home directory and the slash pos. - llvm::StringRef homedir(user_entry->pw_dir); - size_t initial_copy_length = std::min(homedir.size(), replacement_length); - auto src_begin = homedir.begin(); - auto src_end = src_begin + initial_copy_length; - std::copy(src_begin, src_end, path.begin()); - if (replacement_length > homedir.size()) { - // We copied the entire home directory, but the ~username portion of the - // path was - // longer, so there's characters that need to be removed. - path.erase(path.begin() + initial_copy_length, username_end); - } else if (replacement_length < homedir.size()) { - // We copied all the way up to the slash in the destination, but there's - // still more - // characters that need to be inserted. - path.insert(username_end, src_end, homedir.end()); - } - } else { - // Unable to resolve username (user doesn't exist?) - path.clear(); - } -#endif -} - -size_t FileSpec::ResolvePartialUsername(llvm::StringRef partial_name, - StringList &matches) { -#if !defined(LLVM_ON_WIN32) && !defined(__ANDROID__) - size_t extant_entries = matches.GetSize(); - - setpwent(); - struct passwd *user_entry; - partial_name = partial_name.drop_front(); - std::set name_list; - - while ((user_entry = getpwent()) != NULL) { - if (llvm::StringRef(user_entry->pw_name).startswith(partial_name)) { - std::string tmp_buf("~"); - tmp_buf.append(user_entry->pw_name); - tmp_buf.push_back('/'); - name_list.insert(tmp_buf); - } - } - - for (auto &name : name_list) { - matches.AppendString(name); - } - return matches.GetSize() - extant_entries; -#else - // Resolving home directories is not supported, just copy the path... - return 0; -#endif // #ifdef LLVM_ON_WIN32 -} - void FileSpec::Resolve(llvm::SmallVectorImpl &path) { if (path.empty()) return; -#ifndef LLVM_ON_WIN32 - if (path[0] == '~') - ResolveUsername(path); -#endif // #ifdef LLVM_ON_WIN32 + llvm::SmallString<32> Source(path.begin(), path.end()); + StandardTildeExpressionResolver Resolver; + Resolver.ResolveFullPath(Source, path); // Save a copy of the original path that's passed in llvm::SmallString<128> original_path(path.begin(), path.end()); Index: lldb/source/Host/windows/FileSystem.cpp =================================================================== --- lldb/source/Host/windows/FileSystem.cpp +++ lldb/source/Host/windows/FileSystem.cpp @@ -15,6 +15,7 @@ #include "lldb/Host/FileSystem.h" #include "lldb/Host/windows/AutoHandle.h" +#include "lldb/Host/windows/PosixApi.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/FileSystem.h" Index: lldb/source/Host/windows/HostInfoWindows.cpp =================================================================== --- lldb/source/Host/windows/HostInfoWindows.cpp +++ lldb/source/Host/windows/HostInfoWindows.cpp @@ -14,6 +14,7 @@ #include // std::once #include "lldb/Host/windows/HostInfoWindows.h" +#include "lldb/Host/windows/PosixApi.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/FileSystem.h" Index: lldb/source/Target/TargetList.cpp =================================================================== --- lldb/source/Target/TargetList.cpp +++ lldb/source/Target/TargetList.cpp @@ -23,6 +23,7 @@ #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" +#include "lldb/Utility/TildeExpressionResolver.h" // Other libraries and framework includes #include "llvm/ADT/SmallString.h" @@ -347,10 +348,10 @@ FileSpec file(user_exe_path, false); if (!file.Exists() && user_exe_path.startswith("~")) { // we want to expand the tilde but we don't want to resolve any symbolic - // links - // so we can't use the FileSpec constructor's resolve flag - llvm::SmallString<64> unglobbed_path(user_exe_path); - FileSpec::ResolveUsername(unglobbed_path); + // links so we can't use the FileSpec constructor's resolve flag + llvm::SmallString<64> unglobbed_path; + StandardTildeExpressionResolver Resolver; + Resolver.ResolveFullPath(user_exe_path, unglobbed_path); if (unglobbed_path.empty()) file = FileSpec(user_exe_path, false); Index: lldb/source/Utility/TildeExpressionResolver.cpp =================================================================== --- lldb/source/Utility/TildeExpressionResolver.cpp +++ lldb/source/Utility/TildeExpressionResolver.cpp @@ -68,3 +68,22 @@ return true; #endif } + +bool TildeExpressionResolver::ResolveFullPath( + StringRef Expr, llvm::SmallVectorImpl &Output) { + Output.clear(); + if (!Expr.startswith("~")) { + Output.append(Expr.begin(), Expr.end()); + return false; + } + + namespace path = llvm::sys::path; + StringRef Left = + Expr.take_until([](char c) { return path::is_separator(c); }); + + if (!ResolveExact(Left, Output)) + return false; + + Output.append(Expr.begin() + Left.size(), Expr.end()); + return true; +} Index: lldb/unittests/CMakeLists.txt =================================================================== --- lldb/unittests/CMakeLists.txt +++ lldb/unittests/CMakeLists.txt @@ -2,6 +2,7 @@ set_target_properties(LLDBUnitTests PROPERTIES FOLDER "LLDB tests") include_directories(${LLDB_SOURCE_ROOT}) +include_directories(${LLDB_PROJECT_ROOT}) set(LLDB_GTEST_COMMON_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/gtest_common.h) if (MSVC) Index: lldb/unittests/Interpreter/CMakeLists.txt =================================================================== --- lldb/unittests/Interpreter/CMakeLists.txt +++ lldb/unittests/Interpreter/CMakeLists.txt @@ -4,6 +4,7 @@ LINK_LIBS lldbInterpreter + lldbUtilityMocks ) target_link_libraries(InterpreterTests Index: lldb/unittests/Interpreter/TestCompletion.cpp =================================================================== --- lldb/unittests/Interpreter/TestCompletion.cpp +++ lldb/unittests/Interpreter/TestCompletion.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// #include "gtest/gtest.h" - #include "lldb/Core/StringList.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Utility/TildeExpressionResolver.h" @@ -18,6 +17,8 @@ #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" +#include "unittests/Utility/Mocks/MockTildeExpressionResolver.h" + namespace fs = llvm::sys::fs; namespace path = llvm::sys::path; using namespace llvm; @@ -36,74 +37,6 @@ namespace { -class MockTildeExpressionResolver : public TildeExpressionResolver { - StringRef CurrentUser; - StringMap UserDirectories; - -public: - explicit MockTildeExpressionResolver(StringRef CurrentUser, StringRef HomeDir) - : CurrentUser(CurrentUser) { - UserDirectories.insert(std::make_pair(CurrentUser, HomeDir)); - } - - void AddKnownUser(StringRef User, StringRef HomeDir) { - assert(UserDirectories.find(User) == UserDirectories.end()); - UserDirectories.insert(std::make_pair(User, HomeDir)); - } - - void Clear() { - CurrentUser = StringRef(); - UserDirectories.clear(); - } - - void SetCurrentUser(StringRef User) { - assert(UserDirectories.find(User) != UserDirectories.end()); - CurrentUser = User; - } - - bool ResolveExact(StringRef Expr, SmallVectorImpl &Output) override { - Output.clear(); - - assert(!llvm::any_of( - Expr, [](char c) { return llvm::sys::path::is_separator(c); })); - assert(Expr.empty() || Expr[0] == '~'); - Expr = Expr.drop_front(); - if (Expr.empty()) { - auto Dir = UserDirectories[CurrentUser]; - Output.append(Dir.begin(), Dir.end()); - return true; - } - - for (const auto &User : UserDirectories) { - if (User.getKey() != Expr) - continue; - Output.append(User.getValue().begin(), User.getValue().end()); - return true; - } - return false; - } - - bool ResolvePartial(StringRef Expr, StringSet<> &Output) override { - Output.clear(); - - assert(!llvm::any_of( - Expr, [](char c) { return llvm::sys::path::is_separator(c); })); - assert(Expr.empty() || Expr[0] == '~'); - Expr = Expr.drop_front(); - - SmallString<16> QualifiedName("~"); - for (const auto &User : UserDirectories) { - if (!User.getKey().startswith(Expr)) - continue; - QualifiedName.resize(1); - QualifiedName.append(User.getKey().begin(), User.getKey().end()); - Output.insert(QualifiedName); - } - - return !Output.empty(); - } -}; - class CompletionTest : public testing::Test { protected: /// Unique temporary directory in which all created filesystem entities must Index: lldb/unittests/Utility/CMakeLists.txt =================================================================== --- lldb/unittests/Utility/CMakeLists.txt +++ lldb/unittests/Utility/CMakeLists.txt @@ -1,3 +1,5 @@ +add_subdirectory(Mocks) + add_lldb_unittest(UtilityTests ConstStringTest.cpp ErrorTest.cpp @@ -5,12 +7,14 @@ NameMatchesTest.cpp StringExtractorTest.cpp TaskPoolTest.cpp + TildeExpressionResolverTest.cpp TimeoutTest.cpp UriParserTest.cpp VASprintfTest.cpp LINK_LIBS lldbUtility + lldbUtilityMocks LINK_COMPONENTS Support ) Index: lldb/unittests/Utility/Mocks/CMakeLists.txt =================================================================== --- /dev/null +++ lldb/unittests/Utility/Mocks/CMakeLists.txt @@ -0,0 +1,9 @@ +add_lldb_library(lldbUtilityMocks + MockTildeExpressionResolver.cpp + + LINK_LIBS + lldbUtility + + LINK_COMPONENTS + Support + ) Index: lldb/unittests/Utility/Mocks/MockTildeExpressionResolver.h =================================================================== --- /dev/null +++ lldb/unittests/Utility/Mocks/MockTildeExpressionResolver.h @@ -0,0 +1,37 @@ +//===--------------------- TildeExpressionResolver.h ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_UNITTESTS_UTILITY_MOCKS_MOCK_TILDE_EXPRESSION_RESOLVER_H +#define LLDB_UNITTESTS_UTILITY_MOCKS_MOCK_TILDE_EXPRESSION_RESOLVER_H + +#include "lldb/Utility/TildeExpressionResolver.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringMap.h" + +namespace lldb_private { +class MockTildeExpressionResolver : public TildeExpressionResolver { + llvm::StringRef CurrentUser; + llvm::StringMap UserDirectories; + +public: + MockTildeExpressionResolver(llvm::StringRef CurrentUser, + llvm::StringRef HomeDir); + + void AddKnownUser(llvm::StringRef User, llvm::StringRef HomeDir); + void Clear(); + void SetCurrentUser(llvm::StringRef User); + + bool ResolveExact(llvm::StringRef Expr, + llvm::SmallVectorImpl &Output) override; + bool ResolvePartial(llvm::StringRef Expr, llvm::StringSet<> &Output) override; +}; +} + +#endif Index: lldb/unittests/Utility/Mocks/MockTildeExpressionResolver.cpp =================================================================== --- /dev/null +++ lldb/unittests/Utility/Mocks/MockTildeExpressionResolver.cpp @@ -0,0 +1,80 @@ +//===----------------- MockTildeExpressionResolver.cpp ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MockTildeExpressionResolver.h" +#include "llvm/Support/Path.h" + +using namespace lldb_private; +using namespace llvm; + +MockTildeExpressionResolver::MockTildeExpressionResolver(StringRef CurrentUser, + StringRef HomeDir) + : CurrentUser(CurrentUser) { + UserDirectories.insert(std::make_pair(CurrentUser, HomeDir)); +} + +void MockTildeExpressionResolver::AddKnownUser(StringRef User, + StringRef HomeDir) { + assert(UserDirectories.find(User) == UserDirectories.end()); + UserDirectories.insert(std::make_pair(User, HomeDir)); +} + +void MockTildeExpressionResolver::Clear() { + CurrentUser = StringRef(); + UserDirectories.clear(); +} + +void MockTildeExpressionResolver::SetCurrentUser(StringRef User) { + assert(UserDirectories.find(User) != UserDirectories.end()); + CurrentUser = User; +} + +bool MockTildeExpressionResolver::ResolveExact(StringRef Expr, + SmallVectorImpl &Output) { + Output.clear(); + + assert(!llvm::any_of( + Expr, [](char c) { return llvm::sys::path::is_separator(c); })); + assert(Expr.empty() || Expr[0] == '~'); + Expr = Expr.drop_front(); + if (Expr.empty()) { + auto Dir = UserDirectories[CurrentUser]; + Output.append(Dir.begin(), Dir.end()); + return true; + } + + for (const auto &User : UserDirectories) { + if (User.getKey() != Expr) + continue; + Output.append(User.getValue().begin(), User.getValue().end()); + return true; + } + return false; +} + +bool MockTildeExpressionResolver::ResolvePartial(StringRef Expr, + StringSet<> &Output) { + Output.clear(); + + assert(!llvm::any_of( + Expr, [](char c) { return llvm::sys::path::is_separator(c); })); + assert(Expr.empty() || Expr[0] == '~'); + Expr = Expr.drop_front(); + + SmallString<16> QualifiedName("~"); + for (const auto &User : UserDirectories) { + if (!User.getKey().startswith(Expr)) + continue; + QualifiedName.resize(1); + QualifiedName.append(User.getKey().begin(), User.getKey().end()); + Output.insert(QualifiedName); + } + + return !Output.empty(); +} Index: lldb/unittests/Utility/TildeExpressionResolverTest.cpp =================================================================== --- /dev/null +++ lldb/unittests/Utility/TildeExpressionResolverTest.cpp @@ -0,0 +1,36 @@ +#include "gtest/gtest.h" + +#include "Mocks/MockTildeExpressionResolver.h" +#include "lldb/Utility/TildeExpressionResolver.h" + +#include "llvm/ADT/SmallString.h" + +using namespace llvm; +using namespace lldb_private; + +TEST(TildeExpressionResolver, ResolveFullPath) { + MockTildeExpressionResolver Resolver("James", "/james"); + Resolver.AddKnownUser("Kirk", "/kirk"); + Resolver.AddKnownUser("Lars", "/lars"); + Resolver.AddKnownUser("Jason", "/jason"); + Resolver.AddKnownUser("Larry", "/larry"); + + SmallString<32> Result; + ASSERT_TRUE(Resolver.ResolveFullPath("~", Result)); + EXPECT_EQ("/james", Result); + ASSERT_TRUE(Resolver.ResolveFullPath("~/", Result)); + EXPECT_EQ("/james/", Result); + + ASSERT_TRUE(Resolver.ResolveFullPath("~James/bar/baz", Result)); + EXPECT_EQ("/james/bar/baz", Result); + + ASSERT_TRUE(Resolver.ResolveFullPath("~Jason/", Result)); + EXPECT_EQ("/jason/", Result); + + ASSERT_TRUE(Resolver.ResolveFullPath("~Lars", Result)); + EXPECT_EQ("/lars", Result); + + ASSERT_FALSE(Resolver.ResolveFullPath("~Jaso", Result)); + ASSERT_FALSE(Resolver.ResolveFullPath("", Result)); + ASSERT_FALSE(Resolver.ResolveFullPath("Jason", Result)); +}