| //===-- RealpathPrefixes.cpp ----------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "lldb/Utility/RealpathPrefixes.h" |
| |
| #include "lldb/Utility/FileSpec.h" |
| #include "lldb/Utility/FileSpecList.h" |
| #include "lldb/Utility/LLDBLog.h" |
| #include "lldb/Utility/Log.h" |
| #include "lldb/lldb-private-types.h" |
| |
| using namespace lldb_private; |
| |
| RealpathPrefixes::RealpathPrefixes( |
| const FileSpecList &file_spec_list, |
| llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs) |
| : m_fs(fs) { |
| m_prefixes.reserve(file_spec_list.GetSize()); |
| for (const FileSpec &file_spec : file_spec_list) { |
| m_prefixes.emplace_back(file_spec.GetPath()); |
| } |
| } |
| |
| std::optional<FileSpec> |
| RealpathPrefixes::ResolveSymlinks(const FileSpec &file_spec) { |
| if (m_prefixes.empty()) |
| return std::nullopt; |
| |
| // Test if `b` is a *path* prefix of `a` (not just *string* prefix). |
| // E.g. "/foo/bar" is a path prefix of "/foo/bar/baz" but not "/foo/barbaz". |
| auto is_path_prefix = [](llvm::StringRef a, llvm::StringRef b, |
| bool case_sensitive, |
| llvm::sys::path::Style style) -> bool { |
| if (case_sensitive ? a.consume_front(b) : a.consume_front_insensitive(b)) |
| // If `b` isn't "/", then it won't end with "/" because it comes from |
| // `FileSpec`. After `a` consumes `b`, `a` should either be empty (i.e. |
| // `a` == `b`) or end with "/" (the remainder of `a` is a subdirectory). |
| return b == "/" || a.empty() || |
| llvm::sys::path::is_separator(a[0], style); |
| return false; |
| }; |
| std::string file_spec_path = file_spec.GetPath(); |
| for (const std::string &prefix : m_prefixes) { |
| if (is_path_prefix(file_spec_path, prefix, file_spec.IsCaseSensitive(), |
| file_spec.GetPathStyle())) { |
| // Stats and logging. |
| IncreaseSourceRealpathAttemptCount(); |
| Log *log = GetLog(LLDBLog::Source); |
| LLDB_LOGF(log, "Realpath'ing support file %s", file_spec_path.c_str()); |
| |
| // One prefix matched. Try to realpath. |
| PathSmallString buff; |
| std::error_code ec = m_fs->getRealPath(file_spec_path, buff); |
| if (ec) |
| return std::nullopt; |
| FileSpec realpath(buff, file_spec.GetPathStyle()); |
| |
| // Only return realpath if it is different from the original file_spec. |
| if (realpath != file_spec) |
| return realpath; |
| return std::nullopt; |
| } |
| } |
| // No prefix matched |
| return std::nullopt; |
| } |