15ffd83dbSDimitry Andric //===-- FileSpec.cpp ------------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric
90b57cec5SDimitry Andric #include "lldb/Utility/FileSpec.h"
100b57cec5SDimitry Andric #include "lldb/Utility/RegularExpression.h"
110b57cec5SDimitry Andric #include "lldb/Utility/Stream.h"
120b57cec5SDimitry Andric
130b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
140b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h"
15fe013be4SDimitry Andric #include "llvm/ADT/StringExtras.h"
160b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
170b57cec5SDimitry Andric #include "llvm/ADT/Twine.h"
180b57cec5SDimitry Andric #include "llvm/Support/ErrorOr.h"
190b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h"
200b57cec5SDimitry Andric #include "llvm/Support/Program.h"
210b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
22fe013be4SDimitry Andric #include "llvm/TargetParser/Triple.h"
230b57cec5SDimitry Andric
240b57cec5SDimitry Andric #include <algorithm>
25bdd1243dSDimitry Andric #include <optional>
260b57cec5SDimitry Andric #include <system_error>
270b57cec5SDimitry Andric #include <vector>
280b57cec5SDimitry Andric
29fe6060f1SDimitry Andric #include <cassert>
30fe6060f1SDimitry Andric #include <climits>
31fe6060f1SDimitry Andric #include <cstdio>
32fe6060f1SDimitry Andric #include <cstring>
330b57cec5SDimitry Andric
340b57cec5SDimitry Andric using namespace lldb;
350b57cec5SDimitry Andric using namespace lldb_private;
360b57cec5SDimitry Andric
370b57cec5SDimitry Andric namespace {
380b57cec5SDimitry Andric
GetNativeStyle()390b57cec5SDimitry Andric static constexpr FileSpec::Style GetNativeStyle() {
400b57cec5SDimitry Andric #if defined(_WIN32)
410b57cec5SDimitry Andric return FileSpec::Style::windows;
420b57cec5SDimitry Andric #else
430b57cec5SDimitry Andric return FileSpec::Style::posix;
440b57cec5SDimitry Andric #endif
450b57cec5SDimitry Andric }
460b57cec5SDimitry Andric
PathStyleIsPosix(FileSpec::Style style)470b57cec5SDimitry Andric bool PathStyleIsPosix(FileSpec::Style style) {
48349cc55cSDimitry Andric return llvm::sys::path::is_style_posix(style);
490b57cec5SDimitry Andric }
500b57cec5SDimitry Andric
GetPathSeparators(FileSpec::Style style)510b57cec5SDimitry Andric const char *GetPathSeparators(FileSpec::Style style) {
520b57cec5SDimitry Andric return llvm::sys::path::get_separator(style).data();
530b57cec5SDimitry Andric }
540b57cec5SDimitry Andric
GetPreferredPathSeparator(FileSpec::Style style)550b57cec5SDimitry Andric char GetPreferredPathSeparator(FileSpec::Style style) {
560b57cec5SDimitry Andric return GetPathSeparators(style)[0];
570b57cec5SDimitry Andric }
580b57cec5SDimitry Andric
Denormalize(llvm::SmallVectorImpl<char> & path,FileSpec::Style style)590b57cec5SDimitry Andric void Denormalize(llvm::SmallVectorImpl<char> &path, FileSpec::Style style) {
600b57cec5SDimitry Andric if (PathStyleIsPosix(style))
610b57cec5SDimitry Andric return;
620b57cec5SDimitry Andric
630b57cec5SDimitry Andric std::replace(path.begin(), path.end(), '/', '\\');
640b57cec5SDimitry Andric }
650b57cec5SDimitry Andric
660b57cec5SDimitry Andric } // end anonymous namespace
670b57cec5SDimitry Andric
FileSpec()680b57cec5SDimitry Andric FileSpec::FileSpec() : m_style(GetNativeStyle()) {}
690b57cec5SDimitry Andric
700b57cec5SDimitry Andric // Default constructor that can take an optional full path to a file on disk.
FileSpec(llvm::StringRef path,Style style)71*a58f00eaSDimitry Andric FileSpec::FileSpec(llvm::StringRef path, Style style) : m_style(style) {
72*a58f00eaSDimitry Andric SetFile(path, style);
730b57cec5SDimitry Andric }
740b57cec5SDimitry Andric
FileSpec(llvm::StringRef path,const llvm::Triple & triple)759dba64beSDimitry Andric FileSpec::FileSpec(llvm::StringRef path, const llvm::Triple &triple)
769dba64beSDimitry Andric : FileSpec{path, triple.isOSWindows() ? Style::windows : Style::posix} {}
770b57cec5SDimitry Andric
780b57cec5SDimitry Andric namespace {
790b57cec5SDimitry Andric /// Safely get a character at the specified index.
800b57cec5SDimitry Andric ///
810b57cec5SDimitry Andric /// \param[in] path
820b57cec5SDimitry Andric /// A full, partial, or relative path to a file.
830b57cec5SDimitry Andric ///
840b57cec5SDimitry Andric /// \param[in] i
850b57cec5SDimitry Andric /// An index into path which may or may not be valid.
860b57cec5SDimitry Andric ///
870b57cec5SDimitry Andric /// \return
880b57cec5SDimitry Andric /// The character at index \a i if the index is valid, or 0 if
890b57cec5SDimitry Andric /// the index is not valid.
safeCharAtIndex(const llvm::StringRef & path,size_t i)900b57cec5SDimitry Andric inline char safeCharAtIndex(const llvm::StringRef &path, size_t i) {
910b57cec5SDimitry Andric if (i < path.size())
920b57cec5SDimitry Andric return path[i];
930b57cec5SDimitry Andric return 0;
940b57cec5SDimitry Andric }
950b57cec5SDimitry Andric
960b57cec5SDimitry Andric /// Check if a path needs to be normalized.
970b57cec5SDimitry Andric ///
980b57cec5SDimitry Andric /// Check if a path needs to be normalized. We currently consider a
990b57cec5SDimitry Andric /// path to need normalization if any of the following are true
1000b57cec5SDimitry Andric /// - path contains "/./"
1010b57cec5SDimitry Andric /// - path contains "/../"
1020b57cec5SDimitry Andric /// - path contains "//"
1030b57cec5SDimitry Andric /// - path ends with "/"
1040b57cec5SDimitry Andric /// Paths that start with "./" or with "../" are not considered to
1050b57cec5SDimitry Andric /// need normalization since we aren't trying to resolve the path,
1060b57cec5SDimitry Andric /// we are just trying to remove redundant things from the path.
1070b57cec5SDimitry Andric ///
1080b57cec5SDimitry Andric /// \param[in] path
1090b57cec5SDimitry Andric /// A full, partial, or relative path to a file.
1100b57cec5SDimitry Andric ///
1110b57cec5SDimitry Andric /// \return
1120b57cec5SDimitry Andric /// Returns \b true if the path needs to be normalized.
needsNormalization(const llvm::StringRef & path)1130b57cec5SDimitry Andric bool needsNormalization(const llvm::StringRef &path) {
1140b57cec5SDimitry Andric if (path.empty())
1150b57cec5SDimitry Andric return false;
1160b57cec5SDimitry Andric // We strip off leading "." values so these paths need to be normalized
1170b57cec5SDimitry Andric if (path[0] == '.')
1180b57cec5SDimitry Andric return true;
1190b57cec5SDimitry Andric for (auto i = path.find_first_of("\\/"); i != llvm::StringRef::npos;
1200b57cec5SDimitry Andric i = path.find_first_of("\\/", i + 1)) {
1210b57cec5SDimitry Andric const auto next = safeCharAtIndex(path, i+1);
1220b57cec5SDimitry Andric switch (next) {
1230b57cec5SDimitry Andric case 0:
1240b57cec5SDimitry Andric // path separator char at the end of the string which should be
1250b57cec5SDimitry Andric // stripped unless it is the one and only character
1260b57cec5SDimitry Andric return i > 0;
1270b57cec5SDimitry Andric case '/':
1280b57cec5SDimitry Andric case '\\':
1290b57cec5SDimitry Andric // two path separator chars in the middle of a path needs to be
1300b57cec5SDimitry Andric // normalized
1310b57cec5SDimitry Andric if (i > 0)
1320b57cec5SDimitry Andric return true;
1330b57cec5SDimitry Andric ++i;
1340b57cec5SDimitry Andric break;
1350b57cec5SDimitry Andric
1360b57cec5SDimitry Andric case '.': {
1370b57cec5SDimitry Andric const auto next_next = safeCharAtIndex(path, i+2);
1380b57cec5SDimitry Andric switch (next_next) {
1390b57cec5SDimitry Andric default: break;
1400b57cec5SDimitry Andric case 0: return true; // ends with "/."
1410b57cec5SDimitry Andric case '/':
1420b57cec5SDimitry Andric case '\\':
1430b57cec5SDimitry Andric return true; // contains "/./"
1440b57cec5SDimitry Andric case '.': {
1450b57cec5SDimitry Andric const auto next_next_next = safeCharAtIndex(path, i+3);
1460b57cec5SDimitry Andric switch (next_next_next) {
1470b57cec5SDimitry Andric default: break;
1480b57cec5SDimitry Andric case 0: return true; // ends with "/.."
1490b57cec5SDimitry Andric case '/':
1500b57cec5SDimitry Andric case '\\':
1510b57cec5SDimitry Andric return true; // contains "/../"
1520b57cec5SDimitry Andric }
1530b57cec5SDimitry Andric break;
1540b57cec5SDimitry Andric }
1550b57cec5SDimitry Andric }
1560b57cec5SDimitry Andric }
1570b57cec5SDimitry Andric break;
1580b57cec5SDimitry Andric
1590b57cec5SDimitry Andric default:
1600b57cec5SDimitry Andric break;
1610b57cec5SDimitry Andric }
1620b57cec5SDimitry Andric }
1630b57cec5SDimitry Andric return false;
1640b57cec5SDimitry Andric }
1650b57cec5SDimitry Andric
1660b57cec5SDimitry Andric
1670b57cec5SDimitry Andric }
1680b57cec5SDimitry Andric
SetFile(llvm::StringRef pathname)1690b57cec5SDimitry Andric void FileSpec::SetFile(llvm::StringRef pathname) { SetFile(pathname, m_style); }
1700b57cec5SDimitry Andric
1710b57cec5SDimitry Andric // Update the contents of this object with a new path. The path will be split
1720b57cec5SDimitry Andric // up into a directory and filename and stored as uniqued string values for
1730b57cec5SDimitry Andric // quick comparison and efficient memory usage.
SetFile(llvm::StringRef pathname,Style style)174*a58f00eaSDimitry Andric void FileSpec::SetFile(llvm::StringRef pathname, Style style) {
175bdd1243dSDimitry Andric Clear();
1760b57cec5SDimitry Andric m_style = (style == Style::native) ? GetNativeStyle() : style;
1770b57cec5SDimitry Andric
1780b57cec5SDimitry Andric if (pathname.empty())
1790b57cec5SDimitry Andric return;
1800b57cec5SDimitry Andric
1810b57cec5SDimitry Andric llvm::SmallString<128> resolved(pathname);
1820b57cec5SDimitry Andric
1830b57cec5SDimitry Andric // Normalize the path by removing ".", ".." and other redundant components.
1840b57cec5SDimitry Andric if (needsNormalization(resolved))
1850b57cec5SDimitry Andric llvm::sys::path::remove_dots(resolved, true, m_style);
1860b57cec5SDimitry Andric
1870b57cec5SDimitry Andric // Normalize back slashes to forward slashes
1880b57cec5SDimitry Andric if (m_style == Style::windows)
1890b57cec5SDimitry Andric std::replace(resolved.begin(), resolved.end(), '\\', '/');
1900b57cec5SDimitry Andric
1910b57cec5SDimitry Andric if (resolved.empty()) {
1920b57cec5SDimitry Andric // If we have no path after normalization set the path to the current
1930b57cec5SDimitry Andric // directory. This matches what python does and also a few other path
1940b57cec5SDimitry Andric // utilities.
1950b57cec5SDimitry Andric m_filename.SetString(".");
1960b57cec5SDimitry Andric return;
1970b57cec5SDimitry Andric }
1980b57cec5SDimitry Andric
1990b57cec5SDimitry Andric // Split path into filename and directory. We rely on the underlying char
2000b57cec5SDimitry Andric // pointer to be nullptr when the components are empty.
2010b57cec5SDimitry Andric llvm::StringRef filename = llvm::sys::path::filename(resolved, m_style);
2020b57cec5SDimitry Andric if(!filename.empty())
2030b57cec5SDimitry Andric m_filename.SetString(filename);
2040b57cec5SDimitry Andric
2050b57cec5SDimitry Andric llvm::StringRef directory = llvm::sys::path::parent_path(resolved, m_style);
2060b57cec5SDimitry Andric if(!directory.empty())
2070b57cec5SDimitry Andric m_directory.SetString(directory);
2080b57cec5SDimitry Andric }
2090b57cec5SDimitry Andric
SetFile(llvm::StringRef path,const llvm::Triple & triple)2109dba64beSDimitry Andric void FileSpec::SetFile(llvm::StringRef path, const llvm::Triple &triple) {
2119dba64beSDimitry Andric return SetFile(path, triple.isOSWindows() ? Style::windows : Style::posix);
2120b57cec5SDimitry Andric }
2130b57cec5SDimitry Andric
2140b57cec5SDimitry Andric // Convert to pointer operator. This allows code to check any FileSpec objects
2150b57cec5SDimitry Andric // to see if they contain anything valid using code such as:
2160b57cec5SDimitry Andric //
2170b57cec5SDimitry Andric // if (file_spec)
2180b57cec5SDimitry Andric // {}
operator bool() const2190b57cec5SDimitry Andric FileSpec::operator bool() const { return m_filename || m_directory; }
2200b57cec5SDimitry Andric
2210b57cec5SDimitry Andric // Logical NOT operator. This allows code to check any FileSpec objects to see
2220b57cec5SDimitry Andric // if they are invalid using code such as:
2230b57cec5SDimitry Andric //
2240b57cec5SDimitry Andric // if (!file_spec)
2250b57cec5SDimitry Andric // {}
operator !() const2260b57cec5SDimitry Andric bool FileSpec::operator!() const { return !m_directory && !m_filename; }
2270b57cec5SDimitry Andric
DirectoryEquals(const FileSpec & rhs) const2280b57cec5SDimitry Andric bool FileSpec::DirectoryEquals(const FileSpec &rhs) const {
2290b57cec5SDimitry Andric const bool case_sensitive = IsCaseSensitive() || rhs.IsCaseSensitive();
2300b57cec5SDimitry Andric return ConstString::Equals(m_directory, rhs.m_directory, case_sensitive);
2310b57cec5SDimitry Andric }
2320b57cec5SDimitry Andric
FileEquals(const FileSpec & rhs) const2330b57cec5SDimitry Andric bool FileSpec::FileEquals(const FileSpec &rhs) const {
2340b57cec5SDimitry Andric const bool case_sensitive = IsCaseSensitive() || rhs.IsCaseSensitive();
2350b57cec5SDimitry Andric return ConstString::Equals(m_filename, rhs.m_filename, case_sensitive);
2360b57cec5SDimitry Andric }
2370b57cec5SDimitry Andric
2380b57cec5SDimitry Andric // Equal to operator
operator ==(const FileSpec & rhs) const2390b57cec5SDimitry Andric bool FileSpec::operator==(const FileSpec &rhs) const {
2400b57cec5SDimitry Andric return FileEquals(rhs) && DirectoryEquals(rhs);
2410b57cec5SDimitry Andric }
2420b57cec5SDimitry Andric
2430b57cec5SDimitry Andric // Not equal to operator
operator !=(const FileSpec & rhs) const2440b57cec5SDimitry Andric bool FileSpec::operator!=(const FileSpec &rhs) const { return !(*this == rhs); }
2450b57cec5SDimitry Andric
2460b57cec5SDimitry Andric // Less than operator
operator <(const FileSpec & rhs) const2470b57cec5SDimitry Andric bool FileSpec::operator<(const FileSpec &rhs) const {
2480b57cec5SDimitry Andric return FileSpec::Compare(*this, rhs, true) < 0;
2490b57cec5SDimitry Andric }
2500b57cec5SDimitry Andric
2510b57cec5SDimitry Andric // Dump a FileSpec object to a stream
operator <<(Stream & s,const FileSpec & f)2520b57cec5SDimitry Andric Stream &lldb_private::operator<<(Stream &s, const FileSpec &f) {
253480093f4SDimitry Andric f.Dump(s.AsRawOstream());
2540b57cec5SDimitry Andric return s;
2550b57cec5SDimitry Andric }
2560b57cec5SDimitry Andric
2570b57cec5SDimitry Andric // Clear this object by releasing both the directory and filename string values
2580b57cec5SDimitry Andric // and making them both the empty string.
Clear()2590b57cec5SDimitry Andric void FileSpec::Clear() {
2600b57cec5SDimitry Andric m_directory.Clear();
2610b57cec5SDimitry Andric m_filename.Clear();
262bdd1243dSDimitry Andric PathWasModified();
2630b57cec5SDimitry Andric }
2640b57cec5SDimitry Andric
2650b57cec5SDimitry Andric // Compare two FileSpec objects. If "full" is true, then both the directory and
2660b57cec5SDimitry Andric // the filename must match. If "full" is false, then the directory names for
2670b57cec5SDimitry Andric // "a" and "b" are only compared if they are both non-empty. This allows a
2680b57cec5SDimitry Andric // FileSpec object to only contain a filename and it can match FileSpec objects
2690b57cec5SDimitry Andric // that have matching filenames with different paths.
2700b57cec5SDimitry Andric //
2710b57cec5SDimitry Andric // Return -1 if the "a" is less than "b", 0 if "a" is equal to "b" and "1" if
2720b57cec5SDimitry Andric // "a" is greater than "b".
Compare(const FileSpec & a,const FileSpec & b,bool full)2730b57cec5SDimitry Andric int FileSpec::Compare(const FileSpec &a, const FileSpec &b, bool full) {
2740b57cec5SDimitry Andric int result = 0;
2750b57cec5SDimitry Andric
2760b57cec5SDimitry Andric // case sensitivity of compare
2770b57cec5SDimitry Andric const bool case_sensitive = a.IsCaseSensitive() || b.IsCaseSensitive();
2780b57cec5SDimitry Andric
2790b57cec5SDimitry Andric // If full is true, then we must compare both the directory and filename.
2800b57cec5SDimitry Andric
2810b57cec5SDimitry Andric // If full is false, then if either directory is empty, then we match on the
2820b57cec5SDimitry Andric // basename only, and if both directories have valid values, we still do a
2830b57cec5SDimitry Andric // full compare. This allows for matching when we just have a filename in one
2840b57cec5SDimitry Andric // of the FileSpec objects.
2850b57cec5SDimitry Andric
2860b57cec5SDimitry Andric if (full || (a.m_directory && b.m_directory)) {
2870b57cec5SDimitry Andric result = ConstString::Compare(a.m_directory, b.m_directory, case_sensitive);
2880b57cec5SDimitry Andric if (result)
2890b57cec5SDimitry Andric return result;
2900b57cec5SDimitry Andric }
2910b57cec5SDimitry Andric return ConstString::Compare(a.m_filename, b.m_filename, case_sensitive);
2920b57cec5SDimitry Andric }
2930b57cec5SDimitry Andric
Equal(const FileSpec & a,const FileSpec & b,bool full)2940b57cec5SDimitry Andric bool FileSpec::Equal(const FileSpec &a, const FileSpec &b, bool full) {
295480093f4SDimitry Andric if (full || (a.GetDirectory() && b.GetDirectory()))
2960b57cec5SDimitry Andric return a == b;
297480093f4SDimitry Andric
298480093f4SDimitry Andric return a.FileEquals(b);
299480093f4SDimitry Andric }
300480093f4SDimitry Andric
Match(const FileSpec & pattern,const FileSpec & file)301480093f4SDimitry Andric bool FileSpec::Match(const FileSpec &pattern, const FileSpec &file) {
302480093f4SDimitry Andric if (pattern.GetDirectory())
303480093f4SDimitry Andric return pattern == file;
304480093f4SDimitry Andric if (pattern.GetFilename())
305480093f4SDimitry Andric return pattern.FileEquals(file);
306480093f4SDimitry Andric return true;
3070b57cec5SDimitry Andric }
3080b57cec5SDimitry Andric
309bdd1243dSDimitry Andric std::optional<FileSpec::Style>
GuessPathStyle(llvm::StringRef absolute_path)310bdd1243dSDimitry Andric FileSpec::GuessPathStyle(llvm::StringRef absolute_path) {
311c9157d92SDimitry Andric if (absolute_path.starts_with("/"))
3120b57cec5SDimitry Andric return Style::posix;
313c9157d92SDimitry Andric if (absolute_path.starts_with(R"(\\)"))
3140b57cec5SDimitry Andric return Style::windows;
3150eae32dcSDimitry Andric if (absolute_path.size() >= 3 && llvm::isAlpha(absolute_path[0]) &&
31681ad6265SDimitry Andric (absolute_path.substr(1, 2) == R"(:\)" ||
31781ad6265SDimitry Andric absolute_path.substr(1, 2) == R"(:/)"))
3180b57cec5SDimitry Andric return Style::windows;
319bdd1243dSDimitry Andric return std::nullopt;
3200b57cec5SDimitry Andric }
3210b57cec5SDimitry Andric
3220b57cec5SDimitry Andric // Dump the object to the supplied stream. If the object contains a valid
3230b57cec5SDimitry Andric // directory name, it will be displayed followed by a directory delimiter, and
3240b57cec5SDimitry Andric // the filename.
Dump(llvm::raw_ostream & s) const325480093f4SDimitry Andric void FileSpec::Dump(llvm::raw_ostream &s) const {
3260b57cec5SDimitry Andric std::string path{GetPath(true)};
327480093f4SDimitry Andric s << path;
3280b57cec5SDimitry Andric char path_separator = GetPreferredPathSeparator(m_style);
3290b57cec5SDimitry Andric if (!m_filename && !path.empty() && path.back() != path_separator)
330480093f4SDimitry Andric s << path_separator;
3310b57cec5SDimitry Andric }
3320b57cec5SDimitry Andric
GetPathStyle() const3330b57cec5SDimitry Andric FileSpec::Style FileSpec::GetPathStyle() const { return m_style; }
3340b57cec5SDimitry Andric
SetDirectory(ConstString directory)335bdd1243dSDimitry Andric void FileSpec::SetDirectory(ConstString directory) {
336bdd1243dSDimitry Andric m_directory = directory;
337bdd1243dSDimitry Andric PathWasModified();
338bdd1243dSDimitry Andric }
3390b57cec5SDimitry Andric
SetDirectory(llvm::StringRef directory)340bdd1243dSDimitry Andric void FileSpec::SetDirectory(llvm::StringRef directory) {
341bdd1243dSDimitry Andric m_directory = ConstString(directory);
342bdd1243dSDimitry Andric PathWasModified();
343bdd1243dSDimitry Andric }
3440b57cec5SDimitry Andric
SetFilename(ConstString filename)345bdd1243dSDimitry Andric void FileSpec::SetFilename(ConstString filename) {
346bdd1243dSDimitry Andric m_filename = filename;
347bdd1243dSDimitry Andric PathWasModified();
348bdd1243dSDimitry Andric }
3490b57cec5SDimitry Andric
SetFilename(llvm::StringRef filename)350bdd1243dSDimitry Andric void FileSpec::SetFilename(llvm::StringRef filename) {
351bdd1243dSDimitry Andric m_filename = ConstString(filename);
352bdd1243dSDimitry Andric PathWasModified();
353bdd1243dSDimitry Andric }
354bdd1243dSDimitry Andric
ClearFilename()355bdd1243dSDimitry Andric void FileSpec::ClearFilename() {
356bdd1243dSDimitry Andric m_filename.Clear();
357bdd1243dSDimitry Andric PathWasModified();
358bdd1243dSDimitry Andric }
359bdd1243dSDimitry Andric
ClearDirectory()360bdd1243dSDimitry Andric void FileSpec::ClearDirectory() {
361bdd1243dSDimitry Andric m_directory.Clear();
362bdd1243dSDimitry Andric PathWasModified();
363bdd1243dSDimitry Andric }
3640b57cec5SDimitry Andric
3650b57cec5SDimitry Andric // Extract the directory and path into a fixed buffer. This is needed as the
3660b57cec5SDimitry Andric // directory and path are stored in separate string values.
GetPath(char * path,size_t path_max_len,bool denormalize) const3670b57cec5SDimitry Andric size_t FileSpec::GetPath(char *path, size_t path_max_len,
3680b57cec5SDimitry Andric bool denormalize) const {
3690b57cec5SDimitry Andric if (!path)
3700b57cec5SDimitry Andric return 0;
3710b57cec5SDimitry Andric
3720b57cec5SDimitry Andric std::string result = GetPath(denormalize);
3730b57cec5SDimitry Andric ::snprintf(path, path_max_len, "%s", result.c_str());
3740b57cec5SDimitry Andric return std::min(path_max_len - 1, result.length());
3750b57cec5SDimitry Andric }
3760b57cec5SDimitry Andric
GetPath(bool denormalize) const3770b57cec5SDimitry Andric std::string FileSpec::GetPath(bool denormalize) const {
3780b57cec5SDimitry Andric llvm::SmallString<64> result;
3790b57cec5SDimitry Andric GetPath(result, denormalize);
38081ad6265SDimitry Andric return static_cast<std::string>(result);
3810b57cec5SDimitry Andric }
3820b57cec5SDimitry Andric
GetPathAsConstString(bool denormalize) const383bdd1243dSDimitry Andric ConstString FileSpec::GetPathAsConstString(bool denormalize) const {
384bdd1243dSDimitry Andric return ConstString{GetPath(denormalize)};
3850b57cec5SDimitry Andric }
3860b57cec5SDimitry Andric
GetPath(llvm::SmallVectorImpl<char> & path,bool denormalize) const3870b57cec5SDimitry Andric void FileSpec::GetPath(llvm::SmallVectorImpl<char> &path,
3880b57cec5SDimitry Andric bool denormalize) const {
3890b57cec5SDimitry Andric path.append(m_directory.GetStringRef().begin(),
3900b57cec5SDimitry Andric m_directory.GetStringRef().end());
3910b57cec5SDimitry Andric // Since the path was normalized and all paths use '/' when stored in these
3920b57cec5SDimitry Andric // objects, we don't need to look for the actual syntax specific path
3930b57cec5SDimitry Andric // separator, we just look for and insert '/'.
3940b57cec5SDimitry Andric if (m_directory && m_filename && m_directory.GetStringRef().back() != '/' &&
3950b57cec5SDimitry Andric m_filename.GetStringRef().back() != '/')
3960b57cec5SDimitry Andric path.insert(path.end(), '/');
3970b57cec5SDimitry Andric path.append(m_filename.GetStringRef().begin(),
3980b57cec5SDimitry Andric m_filename.GetStringRef().end());
3990b57cec5SDimitry Andric if (denormalize && !path.empty())
4000b57cec5SDimitry Andric Denormalize(path, m_style);
4010b57cec5SDimitry Andric }
4020b57cec5SDimitry Andric
GetFileNameExtension() const403fe013be4SDimitry Andric llvm::StringRef FileSpec::GetFileNameExtension() const {
404fe013be4SDimitry Andric return llvm::sys::path::extension(m_filename.GetStringRef(), m_style);
4050b57cec5SDimitry Andric }
4060b57cec5SDimitry Andric
GetFileNameStrippingExtension() const4070b57cec5SDimitry Andric ConstString FileSpec::GetFileNameStrippingExtension() const {
4080b57cec5SDimitry Andric return ConstString(llvm::sys::path::stem(m_filename.GetStringRef(), m_style));
4090b57cec5SDimitry Andric }
4100b57cec5SDimitry Andric
4110b57cec5SDimitry Andric // Return the size in bytes that this object takes in memory. This returns the
4120b57cec5SDimitry Andric // size in bytes of this object, not any shared string values it may refer to.
MemorySize() const4130b57cec5SDimitry Andric size_t FileSpec::MemorySize() const {
4140b57cec5SDimitry Andric return m_filename.MemorySize() + m_directory.MemorySize();
4150b57cec5SDimitry Andric }
4160b57cec5SDimitry Andric
4170b57cec5SDimitry Andric FileSpec
CopyByAppendingPathComponent(llvm::StringRef component) const4180b57cec5SDimitry Andric FileSpec::CopyByAppendingPathComponent(llvm::StringRef component) const {
4190b57cec5SDimitry Andric FileSpec ret = *this;
4200b57cec5SDimitry Andric ret.AppendPathComponent(component);
4210b57cec5SDimitry Andric return ret;
4220b57cec5SDimitry Andric }
4230b57cec5SDimitry Andric
CopyByRemovingLastPathComponent() const4240b57cec5SDimitry Andric FileSpec FileSpec::CopyByRemovingLastPathComponent() const {
4250b57cec5SDimitry Andric llvm::SmallString<64> current_path;
4260b57cec5SDimitry Andric GetPath(current_path, false);
4270b57cec5SDimitry Andric if (llvm::sys::path::has_parent_path(current_path, m_style))
4280b57cec5SDimitry Andric return FileSpec(llvm::sys::path::parent_path(current_path, m_style),
4290b57cec5SDimitry Andric m_style);
4300b57cec5SDimitry Andric return *this;
4310b57cec5SDimitry Andric }
4320b57cec5SDimitry Andric
PrependPathComponent(llvm::StringRef component)4330b57cec5SDimitry Andric void FileSpec::PrependPathComponent(llvm::StringRef component) {
4340b57cec5SDimitry Andric llvm::SmallString<64> new_path(component);
4350b57cec5SDimitry Andric llvm::SmallString<64> current_path;
4360b57cec5SDimitry Andric GetPath(current_path, false);
4370b57cec5SDimitry Andric llvm::sys::path::append(new_path,
4380b57cec5SDimitry Andric llvm::sys::path::begin(current_path, m_style),
4390b57cec5SDimitry Andric llvm::sys::path::end(current_path), m_style);
4400b57cec5SDimitry Andric SetFile(new_path, m_style);
4410b57cec5SDimitry Andric }
4420b57cec5SDimitry Andric
PrependPathComponent(const FileSpec & new_path)4430b57cec5SDimitry Andric void FileSpec::PrependPathComponent(const FileSpec &new_path) {
4440b57cec5SDimitry Andric return PrependPathComponent(new_path.GetPath(false));
4450b57cec5SDimitry Andric }
4460b57cec5SDimitry Andric
AppendPathComponent(llvm::StringRef component)4470b57cec5SDimitry Andric void FileSpec::AppendPathComponent(llvm::StringRef component) {
4480b57cec5SDimitry Andric llvm::SmallString<64> current_path;
4490b57cec5SDimitry Andric GetPath(current_path, false);
4500b57cec5SDimitry Andric llvm::sys::path::append(current_path, m_style, component);
4510b57cec5SDimitry Andric SetFile(current_path, m_style);
4520b57cec5SDimitry Andric }
4530b57cec5SDimitry Andric
AppendPathComponent(const FileSpec & new_path)4540b57cec5SDimitry Andric void FileSpec::AppendPathComponent(const FileSpec &new_path) {
4550b57cec5SDimitry Andric return AppendPathComponent(new_path.GetPath(false));
4560b57cec5SDimitry Andric }
4570b57cec5SDimitry Andric
RemoveLastPathComponent()4580b57cec5SDimitry Andric bool FileSpec::RemoveLastPathComponent() {
4590b57cec5SDimitry Andric llvm::SmallString<64> current_path;
4600b57cec5SDimitry Andric GetPath(current_path, false);
4610b57cec5SDimitry Andric if (llvm::sys::path::has_parent_path(current_path, m_style)) {
4620b57cec5SDimitry Andric SetFile(llvm::sys::path::parent_path(current_path, m_style));
4630b57cec5SDimitry Andric return true;
4640b57cec5SDimitry Andric }
4650b57cec5SDimitry Andric return false;
4660b57cec5SDimitry Andric }
467fe013be4SDimitry Andric
GetComponents() const468fe013be4SDimitry Andric std::vector<llvm::StringRef> FileSpec::GetComponents() const {
469fe013be4SDimitry Andric std::vector<llvm::StringRef> components;
470fe013be4SDimitry Andric
471fe013be4SDimitry Andric auto dir_begin = llvm::sys::path::begin(m_directory.GetStringRef(), m_style);
472fe013be4SDimitry Andric auto dir_end = llvm::sys::path::end(m_directory.GetStringRef());
473fe013be4SDimitry Andric
474fe013be4SDimitry Andric for (auto iter = dir_begin; iter != dir_end; ++iter) {
475fe013be4SDimitry Andric if (*iter == "/" || *iter == ".")
476fe013be4SDimitry Andric continue;
477fe013be4SDimitry Andric
478fe013be4SDimitry Andric components.push_back(*iter);
479fe013be4SDimitry Andric }
480fe013be4SDimitry Andric
481fe013be4SDimitry Andric if (!m_filename.IsEmpty() && m_filename != "/" && m_filename != ".")
482fe013be4SDimitry Andric components.push_back(m_filename.GetStringRef());
483fe013be4SDimitry Andric
484fe013be4SDimitry Andric return components;
485fe013be4SDimitry Andric }
486fe013be4SDimitry Andric
4870b57cec5SDimitry Andric /// Returns true if the filespec represents an implementation source
4880b57cec5SDimitry Andric /// file (files with a ".c", ".cpp", ".m", ".mm" (many more)
4890b57cec5SDimitry Andric /// extension).
4900b57cec5SDimitry Andric ///
4910b57cec5SDimitry Andric /// \return
4920b57cec5SDimitry Andric /// \b true if the filespec represents an implementation source
4930b57cec5SDimitry Andric /// file, \b false otherwise.
IsSourceImplementationFile() const4940b57cec5SDimitry Andric bool FileSpec::IsSourceImplementationFile() const {
495fe013be4SDimitry Andric llvm::StringRef extension = GetFileNameExtension();
496fe013be4SDimitry Andric if (extension.empty())
4970b57cec5SDimitry Andric return false;
4980b57cec5SDimitry Andric
4990b57cec5SDimitry Andric static RegularExpression g_source_file_regex(llvm::StringRef(
5000b57cec5SDimitry Andric "^.([cC]|[mM]|[mM][mM]|[cC][pP][pP]|[cC]\\+\\+|[cC][xX][xX]|[cC][cC]|["
5010b57cec5SDimitry Andric "cC][pP]|[sS]|[aA][sS][mM]|[fF]|[fF]77|[fF]90|[fF]95|[fF]03|[fF][oO]["
5020b57cec5SDimitry Andric "rR]|[fF][tT][nN]|[fF][pP][pP]|[aA][dD][aA]|[aA][dD][bB]|[aA][dD][sS])"
5030b57cec5SDimitry Andric "$"));
504fe013be4SDimitry Andric return g_source_file_regex.Execute(extension);
5050b57cec5SDimitry Andric }
5060b57cec5SDimitry Andric
IsRelative() const5070b57cec5SDimitry Andric bool FileSpec::IsRelative() const {
5080b57cec5SDimitry Andric return !IsAbsolute();
5090b57cec5SDimitry Andric }
5100b57cec5SDimitry Andric
IsAbsolute() const5110b57cec5SDimitry Andric bool FileSpec::IsAbsolute() const {
512bdd1243dSDimitry Andric // Check if we have cached if this path is absolute to avoid recalculating.
513bdd1243dSDimitry Andric if (m_absolute != Absolute::Calculate)
514bdd1243dSDimitry Andric return m_absolute == Absolute::Yes;
5150b57cec5SDimitry Andric
516bdd1243dSDimitry Andric m_absolute = Absolute::No;
5170b57cec5SDimitry Andric
518bdd1243dSDimitry Andric llvm::SmallString<64> path;
519bdd1243dSDimitry Andric GetPath(path, false);
520bdd1243dSDimitry Andric
521bdd1243dSDimitry Andric if (!path.empty()) {
5220b57cec5SDimitry Andric // We consider paths starting with ~ to be absolute.
523bdd1243dSDimitry Andric if (path[0] == '~' || llvm::sys::path::is_absolute(path, m_style))
524bdd1243dSDimitry Andric m_absolute = Absolute::Yes;
525bdd1243dSDimitry Andric }
5260b57cec5SDimitry Andric
527bdd1243dSDimitry Andric return m_absolute == Absolute::Yes;
5280b57cec5SDimitry Andric }
5290b57cec5SDimitry Andric
MakeAbsolute(const FileSpec & dir)5300b57cec5SDimitry Andric void FileSpec::MakeAbsolute(const FileSpec &dir) {
5310b57cec5SDimitry Andric if (IsRelative())
5320b57cec5SDimitry Andric PrependPathComponent(dir);
5330b57cec5SDimitry Andric }
5340b57cec5SDimitry Andric
format(const FileSpec & F,raw_ostream & Stream,StringRef Style)5350b57cec5SDimitry Andric void llvm::format_provider<FileSpec>::format(const FileSpec &F,
5360b57cec5SDimitry Andric raw_ostream &Stream,
5370b57cec5SDimitry Andric StringRef Style) {
538fe6060f1SDimitry Andric assert((Style.empty() || Style.equals_insensitive("F") ||
539fe6060f1SDimitry Andric Style.equals_insensitive("D")) &&
5400b57cec5SDimitry Andric "Invalid FileSpec style!");
5410b57cec5SDimitry Andric
5420b57cec5SDimitry Andric StringRef dir = F.GetDirectory().GetStringRef();
5430b57cec5SDimitry Andric StringRef file = F.GetFilename().GetStringRef();
5440b57cec5SDimitry Andric
5450b57cec5SDimitry Andric if (dir.empty() && file.empty()) {
5460b57cec5SDimitry Andric Stream << "(empty)";
5470b57cec5SDimitry Andric return;
5480b57cec5SDimitry Andric }
5490b57cec5SDimitry Andric
550fe6060f1SDimitry Andric if (Style.equals_insensitive("F")) {
5510b57cec5SDimitry Andric Stream << (file.empty() ? "(empty)" : file);
5520b57cec5SDimitry Andric return;
5530b57cec5SDimitry Andric }
5540b57cec5SDimitry Andric
5550b57cec5SDimitry Andric // Style is either D or empty, either way we need to print the directory.
5560b57cec5SDimitry Andric if (!dir.empty()) {
5570b57cec5SDimitry Andric // Directory is stored in normalized form, which might be different than
5580b57cec5SDimitry Andric // preferred form. In order to handle this, we need to cut off the
5590b57cec5SDimitry Andric // filename, then denormalize, then write the entire denorm'ed directory.
5600b57cec5SDimitry Andric llvm::SmallString<64> denormalized_dir = dir;
5610b57cec5SDimitry Andric Denormalize(denormalized_dir, F.GetPathStyle());
5620b57cec5SDimitry Andric Stream << denormalized_dir;
5630b57cec5SDimitry Andric Stream << GetPreferredPathSeparator(F.GetPathStyle());
5640b57cec5SDimitry Andric }
5650b57cec5SDimitry Andric
566fe6060f1SDimitry Andric if (Style.equals_insensitive("D")) {
5670b57cec5SDimitry Andric // We only want to print the directory, so now just exit.
5680b57cec5SDimitry Andric if (dir.empty())
5690b57cec5SDimitry Andric Stream << "(empty)";
5700b57cec5SDimitry Andric return;
5710b57cec5SDimitry Andric }
5720b57cec5SDimitry Andric
5730b57cec5SDimitry Andric if (!file.empty())
5740b57cec5SDimitry Andric Stream << file;
5750b57cec5SDimitry Andric }
576