xref: /src/contrib/llvm-project/lldb/source/Utility/FileSpec.cpp (revision 7a6dacaca14b62ca4b74406814becb87a3fefac0)
1cfca06d7SDimitry Andric //===-- FileSpec.cpp ------------------------------------------------------===//
274a628f7SDimitry Andric //
35f29bb8aSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45f29bb8aSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55f29bb8aSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
674a628f7SDimitry Andric //
774a628f7SDimitry Andric //===----------------------------------------------------------------------===//
874a628f7SDimitry Andric 
974a628f7SDimitry Andric #include "lldb/Utility/FileSpec.h"
1074a628f7SDimitry Andric #include "lldb/Utility/RegularExpression.h"
1174a628f7SDimitry Andric #include "lldb/Utility/Stream.h"
1274a628f7SDimitry Andric 
13f73363f1SDimitry Andric #include "llvm/ADT/SmallString.h"
14f73363f1SDimitry Andric #include "llvm/ADT/SmallVector.h"
157fa27ce4SDimitry Andric #include "llvm/ADT/StringExtras.h"
1674a628f7SDimitry Andric #include "llvm/ADT/StringRef.h"
17f73363f1SDimitry Andric #include "llvm/ADT/Twine.h"
18f73363f1SDimitry Andric #include "llvm/Support/ErrorOr.h"
1974a628f7SDimitry Andric #include "llvm/Support/FileSystem.h"
2074a628f7SDimitry Andric #include "llvm/Support/Program.h"
21f73363f1SDimitry Andric #include "llvm/Support/raw_ostream.h"
227fa27ce4SDimitry Andric #include "llvm/TargetParser/Triple.h"
2374a628f7SDimitry Andric 
2494994d37SDimitry Andric #include <algorithm>
25e3b55780SDimitry Andric #include <optional>
2694994d37SDimitry Andric #include <system_error>
2794994d37SDimitry Andric #include <vector>
2874a628f7SDimitry Andric 
29344a3780SDimitry Andric #include <cassert>
30344a3780SDimitry Andric #include <climits>
31344a3780SDimitry Andric #include <cstdio>
32344a3780SDimitry Andric #include <cstring>
3374a628f7SDimitry Andric 
3474a628f7SDimitry Andric using namespace lldb;
3574a628f7SDimitry Andric using namespace lldb_private;
3674a628f7SDimitry Andric 
3774a628f7SDimitry Andric namespace {
3874a628f7SDimitry Andric 
GetNativeStyle()39f73363f1SDimitry Andric static constexpr FileSpec::Style GetNativeStyle() {
40f73363f1SDimitry Andric #if defined(_WIN32)
41f73363f1SDimitry Andric   return FileSpec::Style::windows;
4274a628f7SDimitry Andric #else
43f73363f1SDimitry Andric   return FileSpec::Style::posix;
4474a628f7SDimitry Andric #endif
4574a628f7SDimitry Andric }
4674a628f7SDimitry Andric 
PathStyleIsPosix(FileSpec::Style style)47f73363f1SDimitry Andric bool PathStyleIsPosix(FileSpec::Style style) {
48c0981da4SDimitry Andric   return llvm::sys::path::is_style_posix(style);
4974a628f7SDimitry Andric }
5074a628f7SDimitry Andric 
GetPathSeparators(FileSpec::Style style)51f73363f1SDimitry Andric const char *GetPathSeparators(FileSpec::Style style) {
52f73363f1SDimitry Andric   return llvm::sys::path::get_separator(style).data();
5374a628f7SDimitry Andric }
5474a628f7SDimitry Andric 
GetPreferredPathSeparator(FileSpec::Style style)55f73363f1SDimitry Andric char GetPreferredPathSeparator(FileSpec::Style style) {
56f73363f1SDimitry Andric   return GetPathSeparators(style)[0];
5774a628f7SDimitry Andric }
5874a628f7SDimitry Andric 
Denormalize(llvm::SmallVectorImpl<char> & path,FileSpec::Style style)59f73363f1SDimitry Andric void Denormalize(llvm::SmallVectorImpl<char> &path, FileSpec::Style style) {
60f73363f1SDimitry Andric   if (PathStyleIsPosix(style))
6174a628f7SDimitry Andric     return;
6274a628f7SDimitry Andric 
6374a628f7SDimitry Andric   std::replace(path.begin(), path.end(), '/', '\\');
6474a628f7SDimitry Andric }
6574a628f7SDimitry Andric 
6674a628f7SDimitry Andric } // end anonymous namespace
6774a628f7SDimitry Andric 
FileSpec()68f73363f1SDimitry Andric FileSpec::FileSpec() : m_style(GetNativeStyle()) {}
6974a628f7SDimitry Andric 
70f73363f1SDimitry Andric // Default constructor that can take an optional full path to a file on disk.
FileSpec(llvm::StringRef path,Style style)714df029ccSDimitry Andric FileSpec::FileSpec(llvm::StringRef path, Style style) : m_style(style) {
724df029ccSDimitry Andric   SetFile(path, style);
7374a628f7SDimitry Andric }
7474a628f7SDimitry Andric 
FileSpec(llvm::StringRef path,const llvm::Triple & triple)75ead24645SDimitry Andric FileSpec::FileSpec(llvm::StringRef path, const llvm::Triple &triple)
76ead24645SDimitry Andric     : FileSpec{path, triple.isOSWindows() ? Style::windows : Style::posix} {}
7774a628f7SDimitry Andric 
78f73363f1SDimitry Andric namespace {
79f73363f1SDimitry Andric /// Safely get a character at the specified index.
80f73363f1SDimitry Andric ///
815f29bb8aSDimitry Andric /// \param[in] path
82f73363f1SDimitry Andric ///     A full, partial, or relative path to a file.
83f73363f1SDimitry Andric ///
845f29bb8aSDimitry Andric /// \param[in] i
85f73363f1SDimitry Andric ///     An index into path which may or may not be valid.
86f73363f1SDimitry Andric ///
875f29bb8aSDimitry Andric /// \return
88f73363f1SDimitry Andric ///   The character at index \a i if the index is valid, or 0 if
89f73363f1SDimitry Andric ///   the index is not valid.
safeCharAtIndex(const llvm::StringRef & path,size_t i)90f73363f1SDimitry Andric inline char safeCharAtIndex(const llvm::StringRef &path, size_t i) {
91f73363f1SDimitry Andric   if (i < path.size())
92f73363f1SDimitry Andric     return path[i];
93f73363f1SDimitry Andric   return 0;
94f73363f1SDimitry Andric }
95f73363f1SDimitry Andric 
96f73363f1SDimitry Andric /// Check if a path needs to be normalized.
97f73363f1SDimitry Andric ///
98f73363f1SDimitry Andric /// Check if a path needs to be normalized. We currently consider a
99f73363f1SDimitry Andric /// path to need normalization if any of the following are true
100f73363f1SDimitry Andric ///  - path contains "/./"
101f73363f1SDimitry Andric ///  - path contains "/../"
102f73363f1SDimitry Andric ///  - path contains "//"
103f73363f1SDimitry Andric ///  - path ends with "/"
104f73363f1SDimitry Andric /// Paths that start with "./" or with "../" are not considered to
105f73363f1SDimitry Andric /// need normalization since we aren't trying to resolve the path,
106f73363f1SDimitry Andric /// we are just trying to remove redundant things from the path.
107f73363f1SDimitry Andric ///
1085f29bb8aSDimitry Andric /// \param[in] path
109f73363f1SDimitry Andric ///     A full, partial, or relative path to a file.
110f73363f1SDimitry Andric ///
1115f29bb8aSDimitry Andric /// \return
112f73363f1SDimitry Andric ///   Returns \b true if the path needs to be normalized.
needsNormalization(const llvm::StringRef & path)113f73363f1SDimitry Andric bool needsNormalization(const llvm::StringRef &path) {
114f73363f1SDimitry Andric   if (path.empty())
115f73363f1SDimitry Andric     return false;
116f73363f1SDimitry Andric   // We strip off leading "." values so these paths need to be normalized
117f73363f1SDimitry Andric   if (path[0] == '.')
118f73363f1SDimitry Andric     return true;
119f73363f1SDimitry Andric   for (auto i = path.find_first_of("\\/"); i != llvm::StringRef::npos;
120f73363f1SDimitry Andric        i = path.find_first_of("\\/", i + 1)) {
121f73363f1SDimitry Andric     const auto next = safeCharAtIndex(path, i+1);
122f73363f1SDimitry Andric     switch (next) {
123f73363f1SDimitry Andric       case 0:
124f73363f1SDimitry Andric         // path separator char at the end of the string which should be
125f73363f1SDimitry Andric         // stripped unless it is the one and only character
126f73363f1SDimitry Andric         return i > 0;
127f73363f1SDimitry Andric       case '/':
128f73363f1SDimitry Andric       case '\\':
129f73363f1SDimitry Andric         // two path separator chars in the middle of a path needs to be
130f73363f1SDimitry Andric         // normalized
131f73363f1SDimitry Andric         if (i > 0)
132f73363f1SDimitry Andric           return true;
133f73363f1SDimitry Andric         ++i;
134f73363f1SDimitry Andric         break;
135f73363f1SDimitry Andric 
136f73363f1SDimitry Andric       case '.': {
137f73363f1SDimitry Andric           const auto next_next = safeCharAtIndex(path, i+2);
138f73363f1SDimitry Andric           switch (next_next) {
139f73363f1SDimitry Andric             default: break;
140f73363f1SDimitry Andric             case 0: return true; // ends with "/."
141f73363f1SDimitry Andric             case '/':
142f73363f1SDimitry Andric             case '\\':
143f73363f1SDimitry Andric               return true; // contains "/./"
144f73363f1SDimitry Andric             case '.': {
145f73363f1SDimitry Andric               const auto next_next_next = safeCharAtIndex(path, i+3);
146f73363f1SDimitry Andric               switch (next_next_next) {
147f73363f1SDimitry Andric                 default: break;
148f73363f1SDimitry Andric                 case 0: return true; // ends with "/.."
149f73363f1SDimitry Andric                 case '/':
150f73363f1SDimitry Andric                 case '\\':
151f73363f1SDimitry Andric                   return true; // contains "/../"
152f73363f1SDimitry Andric               }
153f73363f1SDimitry Andric               break;
154f73363f1SDimitry Andric             }
155f73363f1SDimitry Andric           }
156f73363f1SDimitry Andric         }
157f73363f1SDimitry Andric         break;
158f73363f1SDimitry Andric 
159f73363f1SDimitry Andric       default:
160f73363f1SDimitry Andric         break;
161f73363f1SDimitry Andric     }
162f73363f1SDimitry Andric   }
163f73363f1SDimitry Andric   return false;
164f73363f1SDimitry Andric }
165f73363f1SDimitry Andric 
166f73363f1SDimitry Andric 
167f73363f1SDimitry Andric }
16874a628f7SDimitry Andric 
SetFile(llvm::StringRef pathname)16994994d37SDimitry Andric void FileSpec::SetFile(llvm::StringRef pathname) { SetFile(pathname, m_style); }
170f73363f1SDimitry Andric 
171f73363f1SDimitry Andric // Update the contents of this object with a new path. The path will be split
172f73363f1SDimitry Andric // up into a directory and filename and stored as uniqued string values for
173f73363f1SDimitry Andric // quick comparison and efficient memory usage.
SetFile(llvm::StringRef pathname,Style style)1744df029ccSDimitry Andric void FileSpec::SetFile(llvm::StringRef pathname, Style style) {
175e3b55780SDimitry Andric   Clear();
176f73363f1SDimitry Andric   m_style = (style == Style::native) ? GetNativeStyle() : style;
17774a628f7SDimitry Andric 
17874a628f7SDimitry Andric   if (pathname.empty())
17974a628f7SDimitry Andric     return;
18074a628f7SDimitry Andric 
18194994d37SDimitry Andric   llvm::SmallString<128> resolved(pathname);
18274a628f7SDimitry Andric 
183f73363f1SDimitry Andric   // Normalize the path by removing ".", ".." and other redundant components.
184f73363f1SDimitry Andric   if (needsNormalization(resolved))
185f73363f1SDimitry Andric     llvm::sys::path::remove_dots(resolved, true, m_style);
18674a628f7SDimitry Andric 
187f73363f1SDimitry Andric   // Normalize back slashes to forward slashes
188f73363f1SDimitry Andric   if (m_style == Style::windows)
189f73363f1SDimitry Andric     std::replace(resolved.begin(), resolved.end(), '\\', '/');
190f73363f1SDimitry Andric 
191f73363f1SDimitry Andric   if (resolved.empty()) {
192f73363f1SDimitry Andric     // If we have no path after normalization set the path to the current
193f73363f1SDimitry Andric     // directory. This matches what python does and also a few other path
194f73363f1SDimitry Andric     // utilities.
195f73363f1SDimitry Andric     m_filename.SetString(".");
19674a628f7SDimitry Andric     return;
19774a628f7SDimitry Andric   }
19874a628f7SDimitry Andric 
199f73363f1SDimitry Andric   // Split path into filename and directory. We rely on the underlying char
200f73363f1SDimitry Andric   // pointer to be nullptr when the components are empty.
201f73363f1SDimitry Andric   llvm::StringRef filename = llvm::sys::path::filename(resolved, m_style);
202f73363f1SDimitry Andric   if(!filename.empty())
203f73363f1SDimitry Andric     m_filename.SetString(filename);
20494994d37SDimitry Andric 
205f73363f1SDimitry Andric   llvm::StringRef directory = llvm::sys::path::parent_path(resolved, m_style);
206f73363f1SDimitry Andric   if(!directory.empty())
207f73363f1SDimitry Andric     m_directory.SetString(directory);
20874a628f7SDimitry Andric }
20974a628f7SDimitry Andric 
SetFile(llvm::StringRef path,const llvm::Triple & triple)210ead24645SDimitry Andric void FileSpec::SetFile(llvm::StringRef path, const llvm::Triple &triple) {
211ead24645SDimitry Andric   return SetFile(path, triple.isOSWindows() ? Style::windows : Style::posix);
21274a628f7SDimitry Andric }
21374a628f7SDimitry Andric 
214f73363f1SDimitry Andric // Convert to pointer operator. This allows code to check any FileSpec objects
215f73363f1SDimitry Andric // to see if they contain anything valid using code such as:
21674a628f7SDimitry Andric //
21774a628f7SDimitry Andric //  if (file_spec)
21874a628f7SDimitry Andric //  {}
operator bool() const21974a628f7SDimitry Andric FileSpec::operator bool() const { return m_filename || m_directory; }
22074a628f7SDimitry Andric 
221f73363f1SDimitry Andric // Logical NOT operator. This allows code to check any FileSpec objects to see
222f73363f1SDimitry Andric // if they are invalid using code such as:
22374a628f7SDimitry Andric //
22474a628f7SDimitry Andric //  if (!file_spec)
22574a628f7SDimitry Andric //  {}
operator !() const22674a628f7SDimitry Andric bool FileSpec::operator!() const { return !m_directory && !m_filename; }
22774a628f7SDimitry Andric 
DirectoryEquals(const FileSpec & rhs) const22874a628f7SDimitry Andric bool FileSpec::DirectoryEquals(const FileSpec &rhs) const {
22974a628f7SDimitry Andric   const bool case_sensitive = IsCaseSensitive() || rhs.IsCaseSensitive();
23074a628f7SDimitry Andric   return ConstString::Equals(m_directory, rhs.m_directory, case_sensitive);
23174a628f7SDimitry Andric }
23274a628f7SDimitry Andric 
FileEquals(const FileSpec & rhs) const23374a628f7SDimitry Andric bool FileSpec::FileEquals(const FileSpec &rhs) const {
23474a628f7SDimitry Andric   const bool case_sensitive = IsCaseSensitive() || rhs.IsCaseSensitive();
23574a628f7SDimitry Andric   return ConstString::Equals(m_filename, rhs.m_filename, case_sensitive);
23674a628f7SDimitry Andric }
23774a628f7SDimitry Andric 
23874a628f7SDimitry Andric // Equal to operator
operator ==(const FileSpec & rhs) const23974a628f7SDimitry Andric bool FileSpec::operator==(const FileSpec &rhs) const {
24094994d37SDimitry Andric   return FileEquals(rhs) && DirectoryEquals(rhs);
24174a628f7SDimitry Andric }
24274a628f7SDimitry Andric 
24374a628f7SDimitry Andric // Not equal to operator
operator !=(const FileSpec & rhs) const24474a628f7SDimitry Andric bool FileSpec::operator!=(const FileSpec &rhs) const { return !(*this == rhs); }
24574a628f7SDimitry Andric 
24674a628f7SDimitry Andric // Less than operator
operator <(const FileSpec & rhs) const24774a628f7SDimitry Andric bool FileSpec::operator<(const FileSpec &rhs) const {
24874a628f7SDimitry Andric   return FileSpec::Compare(*this, rhs, true) < 0;
24974a628f7SDimitry Andric }
25074a628f7SDimitry Andric 
25174a628f7SDimitry Andric // Dump a FileSpec object to a stream
operator <<(Stream & s,const FileSpec & f)25274a628f7SDimitry Andric Stream &lldb_private::operator<<(Stream &s, const FileSpec &f) {
253706b4fc4SDimitry Andric   f.Dump(s.AsRawOstream());
25474a628f7SDimitry Andric   return s;
25574a628f7SDimitry Andric }
25674a628f7SDimitry Andric 
257f73363f1SDimitry Andric // Clear this object by releasing both the directory and filename string values
258f73363f1SDimitry Andric // and making them both the empty string.
Clear()25974a628f7SDimitry Andric void FileSpec::Clear() {
26074a628f7SDimitry Andric   m_directory.Clear();
26174a628f7SDimitry Andric   m_filename.Clear();
262e3b55780SDimitry Andric   PathWasModified();
26374a628f7SDimitry Andric }
26474a628f7SDimitry Andric 
265f73363f1SDimitry Andric // Compare two FileSpec objects. If "full" is true, then both the directory and
266f73363f1SDimitry Andric // the filename must match. If "full" is false, then the directory names for
267f73363f1SDimitry Andric // "a" and "b" are only compared if they are both non-empty. This allows a
268f73363f1SDimitry Andric // FileSpec object to only contain a filename and it can match FileSpec objects
269f73363f1SDimitry Andric // that have matching filenames with different paths.
27074a628f7SDimitry Andric //
271f73363f1SDimitry Andric // Return -1 if the "a" is less than "b", 0 if "a" is equal to "b" and "1" if
272f73363f1SDimitry Andric // "a" is greater than "b".
Compare(const FileSpec & a,const FileSpec & b,bool full)27374a628f7SDimitry Andric int FileSpec::Compare(const FileSpec &a, const FileSpec &b, bool full) {
27474a628f7SDimitry Andric   int result = 0;
27574a628f7SDimitry Andric 
27674a628f7SDimitry Andric   // case sensitivity of compare
27774a628f7SDimitry Andric   const bool case_sensitive = a.IsCaseSensitive() || b.IsCaseSensitive();
27874a628f7SDimitry Andric 
27974a628f7SDimitry Andric   // If full is true, then we must compare both the directory and filename.
28074a628f7SDimitry Andric 
281f73363f1SDimitry Andric   // If full is false, then if either directory is empty, then we match on the
282f73363f1SDimitry Andric   // basename only, and if both directories have valid values, we still do a
283f73363f1SDimitry Andric   // full compare. This allows for matching when we just have a filename in one
284f73363f1SDimitry Andric   // of the FileSpec objects.
28574a628f7SDimitry Andric 
28674a628f7SDimitry Andric   if (full || (a.m_directory && b.m_directory)) {
28774a628f7SDimitry Andric     result = ConstString::Compare(a.m_directory, b.m_directory, case_sensitive);
28874a628f7SDimitry Andric     if (result)
28974a628f7SDimitry Andric       return result;
29074a628f7SDimitry Andric   }
29174a628f7SDimitry Andric   return ConstString::Compare(a.m_filename, b.m_filename, case_sensitive);
29274a628f7SDimitry Andric }
29374a628f7SDimitry Andric 
Equal(const FileSpec & a,const FileSpec & b,bool full)294f73363f1SDimitry Andric bool FileSpec::Equal(const FileSpec &a, const FileSpec &b, bool full) {
295706b4fc4SDimitry Andric   if (full || (a.GetDirectory() && b.GetDirectory()))
29674a628f7SDimitry Andric     return a == b;
297706b4fc4SDimitry Andric 
298706b4fc4SDimitry Andric   return a.FileEquals(b);
299706b4fc4SDimitry Andric }
300706b4fc4SDimitry Andric 
Match(const FileSpec & pattern,const FileSpec & file)301706b4fc4SDimitry Andric bool FileSpec::Match(const FileSpec &pattern, const FileSpec &file) {
302706b4fc4SDimitry Andric   if (pattern.GetDirectory())
303706b4fc4SDimitry Andric     return pattern == file;
304706b4fc4SDimitry Andric   if (pattern.GetFilename())
305706b4fc4SDimitry Andric     return pattern.FileEquals(file);
306706b4fc4SDimitry Andric   return true;
30774a628f7SDimitry Andric }
30874a628f7SDimitry Andric 
309e3b55780SDimitry Andric std::optional<FileSpec::Style>
GuessPathStyle(llvm::StringRef absolute_path)310e3b55780SDimitry Andric FileSpec::GuessPathStyle(llvm::StringRef absolute_path) {
311312c0ed1SDimitry Andric   if (absolute_path.starts_with("/"))
3125f29bb8aSDimitry Andric     return Style::posix;
313312c0ed1SDimitry Andric   if (absolute_path.starts_with(R"(\\)"))
3145f29bb8aSDimitry Andric     return Style::windows;
31577fc4c14SDimitry Andric   if (absolute_path.size() >= 3 && llvm::isAlpha(absolute_path[0]) &&
316145449b1SDimitry Andric       (absolute_path.substr(1, 2) == R"(:\)" ||
317145449b1SDimitry Andric        absolute_path.substr(1, 2) == R"(:/)"))
3185f29bb8aSDimitry Andric     return Style::windows;
319e3b55780SDimitry Andric   return std::nullopt;
3205f29bb8aSDimitry Andric }
3215f29bb8aSDimitry Andric 
322f73363f1SDimitry Andric // Dump the object to the supplied stream. If the object contains a valid
323f73363f1SDimitry Andric // directory name, it will be displayed followed by a directory delimiter, and
324f73363f1SDimitry Andric // the filename.
Dump(llvm::raw_ostream & s) const325706b4fc4SDimitry Andric void FileSpec::Dump(llvm::raw_ostream &s) const {
32674a628f7SDimitry Andric   std::string path{GetPath(true)};
327706b4fc4SDimitry Andric   s << path;
328f73363f1SDimitry Andric   char path_separator = GetPreferredPathSeparator(m_style);
32974a628f7SDimitry Andric   if (!m_filename && !path.empty() && path.back() != path_separator)
330706b4fc4SDimitry Andric     s << path_separator;
33174a628f7SDimitry Andric }
33274a628f7SDimitry Andric 
GetPathStyle() const333f73363f1SDimitry Andric FileSpec::Style FileSpec::GetPathStyle() const { return m_style; }
33474a628f7SDimitry Andric 
SetDirectory(ConstString directory)335e3b55780SDimitry Andric void FileSpec::SetDirectory(ConstString directory) {
336e3b55780SDimitry Andric   m_directory = directory;
337e3b55780SDimitry Andric   PathWasModified();
338e3b55780SDimitry Andric }
33974a628f7SDimitry Andric 
SetDirectory(llvm::StringRef directory)340e3b55780SDimitry Andric void FileSpec::SetDirectory(llvm::StringRef directory) {
341e3b55780SDimitry Andric   m_directory = ConstString(directory);
342e3b55780SDimitry Andric   PathWasModified();
343e3b55780SDimitry Andric }
34474a628f7SDimitry Andric 
SetFilename(ConstString filename)345e3b55780SDimitry Andric void FileSpec::SetFilename(ConstString filename) {
346e3b55780SDimitry Andric   m_filename = filename;
347e3b55780SDimitry Andric   PathWasModified();
348e3b55780SDimitry Andric }
34974a628f7SDimitry Andric 
SetFilename(llvm::StringRef filename)350e3b55780SDimitry Andric void FileSpec::SetFilename(llvm::StringRef filename) {
351e3b55780SDimitry Andric   m_filename = ConstString(filename);
352e3b55780SDimitry Andric   PathWasModified();
353e3b55780SDimitry Andric }
354e3b55780SDimitry Andric 
ClearFilename()355e3b55780SDimitry Andric void FileSpec::ClearFilename() {
356e3b55780SDimitry Andric   m_filename.Clear();
357e3b55780SDimitry Andric   PathWasModified();
358e3b55780SDimitry Andric }
359e3b55780SDimitry Andric 
ClearDirectory()360e3b55780SDimitry Andric void FileSpec::ClearDirectory() {
361e3b55780SDimitry Andric   m_directory.Clear();
362e3b55780SDimitry Andric   PathWasModified();
363e3b55780SDimitry Andric }
36474a628f7SDimitry Andric 
365f73363f1SDimitry Andric // Extract the directory and path into a fixed buffer. This is needed as the
366f73363f1SDimitry Andric // directory and path are stored in separate string values.
GetPath(char * path,size_t path_max_len,bool denormalize) const36774a628f7SDimitry Andric size_t FileSpec::GetPath(char *path, size_t path_max_len,
36874a628f7SDimitry Andric                          bool denormalize) const {
36974a628f7SDimitry Andric   if (!path)
37074a628f7SDimitry Andric     return 0;
37174a628f7SDimitry Andric 
37274a628f7SDimitry Andric   std::string result = GetPath(denormalize);
37374a628f7SDimitry Andric   ::snprintf(path, path_max_len, "%s", result.c_str());
37474a628f7SDimitry Andric   return std::min(path_max_len - 1, result.length());
37574a628f7SDimitry Andric }
37674a628f7SDimitry Andric 
GetPath(bool denormalize) const37774a628f7SDimitry Andric std::string FileSpec::GetPath(bool denormalize) const {
37874a628f7SDimitry Andric   llvm::SmallString<64> result;
37974a628f7SDimitry Andric   GetPath(result, denormalize);
380145449b1SDimitry Andric   return static_cast<std::string>(result);
38174a628f7SDimitry Andric }
38274a628f7SDimitry Andric 
GetPathAsConstString(bool denormalize) const383e3b55780SDimitry Andric ConstString FileSpec::GetPathAsConstString(bool denormalize) const {
384e3b55780SDimitry Andric   return ConstString{GetPath(denormalize)};
38574a628f7SDimitry Andric }
38674a628f7SDimitry Andric 
GetPath(llvm::SmallVectorImpl<char> & path,bool denormalize) const38774a628f7SDimitry Andric void FileSpec::GetPath(llvm::SmallVectorImpl<char> &path,
38874a628f7SDimitry Andric                        bool denormalize) const {
38974a628f7SDimitry Andric   path.append(m_directory.GetStringRef().begin(),
39074a628f7SDimitry Andric               m_directory.GetStringRef().end());
391f73363f1SDimitry Andric   // Since the path was normalized and all paths use '/' when stored in these
392f73363f1SDimitry Andric   // objects, we don't need to look for the actual syntax specific path
393f73363f1SDimitry Andric   // separator, we just look for and insert '/'.
394f73363f1SDimitry Andric   if (m_directory && m_filename && m_directory.GetStringRef().back() != '/' &&
395f73363f1SDimitry Andric       m_filename.GetStringRef().back() != '/')
396f73363f1SDimitry Andric     path.insert(path.end(), '/');
39774a628f7SDimitry Andric   path.append(m_filename.GetStringRef().begin(),
39874a628f7SDimitry Andric               m_filename.GetStringRef().end());
39974a628f7SDimitry Andric   if (denormalize && !path.empty())
400f73363f1SDimitry Andric     Denormalize(path, m_style);
40174a628f7SDimitry Andric }
40274a628f7SDimitry Andric 
GetFileNameExtension() const4037fa27ce4SDimitry Andric llvm::StringRef FileSpec::GetFileNameExtension() const {
4047fa27ce4SDimitry Andric   return llvm::sys::path::extension(m_filename.GetStringRef(), m_style);
40574a628f7SDimitry Andric }
40674a628f7SDimitry Andric 
GetFileNameStrippingExtension() const40774a628f7SDimitry Andric ConstString FileSpec::GetFileNameStrippingExtension() const {
408f73363f1SDimitry Andric   return ConstString(llvm::sys::path::stem(m_filename.GetStringRef(), m_style));
40974a628f7SDimitry Andric }
41074a628f7SDimitry Andric 
411f73363f1SDimitry Andric // Return the size in bytes that this object takes in memory. This returns the
412f73363f1SDimitry Andric // size in bytes of this object, not any shared string values it may refer to.
MemorySize() const41374a628f7SDimitry Andric size_t FileSpec::MemorySize() const {
41474a628f7SDimitry Andric   return m_filename.MemorySize() + m_directory.MemorySize();
41574a628f7SDimitry Andric }
41674a628f7SDimitry Andric 
41774a628f7SDimitry Andric FileSpec
CopyByAppendingPathComponent(llvm::StringRef component) const41874a628f7SDimitry Andric FileSpec::CopyByAppendingPathComponent(llvm::StringRef component) const {
41974a628f7SDimitry Andric   FileSpec ret = *this;
42074a628f7SDimitry Andric   ret.AppendPathComponent(component);
42174a628f7SDimitry Andric   return ret;
42274a628f7SDimitry Andric }
42374a628f7SDimitry Andric 
CopyByRemovingLastPathComponent() const42474a628f7SDimitry Andric FileSpec FileSpec::CopyByRemovingLastPathComponent() const {
425f73363f1SDimitry Andric   llvm::SmallString<64> current_path;
426f73363f1SDimitry Andric   GetPath(current_path, false);
427f73363f1SDimitry Andric   if (llvm::sys::path::has_parent_path(current_path, m_style))
42894994d37SDimitry Andric     return FileSpec(llvm::sys::path::parent_path(current_path, m_style),
429f73363f1SDimitry Andric                     m_style);
430f73363f1SDimitry Andric   return *this;
43174a628f7SDimitry Andric }
43274a628f7SDimitry Andric 
PrependPathComponent(llvm::StringRef component)43374a628f7SDimitry Andric void FileSpec::PrependPathComponent(llvm::StringRef component) {
434f73363f1SDimitry Andric   llvm::SmallString<64> new_path(component);
435f73363f1SDimitry Andric   llvm::SmallString<64> current_path;
436f73363f1SDimitry Andric   GetPath(current_path, false);
437f73363f1SDimitry Andric   llvm::sys::path::append(new_path,
438f73363f1SDimitry Andric                           llvm::sys::path::begin(current_path, m_style),
439f73363f1SDimitry Andric                           llvm::sys::path::end(current_path), m_style);
44094994d37SDimitry Andric   SetFile(new_path, m_style);
44174a628f7SDimitry Andric }
44274a628f7SDimitry Andric 
PrependPathComponent(const FileSpec & new_path)44374a628f7SDimitry Andric void FileSpec::PrependPathComponent(const FileSpec &new_path) {
44474a628f7SDimitry Andric   return PrependPathComponent(new_path.GetPath(false));
44574a628f7SDimitry Andric }
44674a628f7SDimitry Andric 
AppendPathComponent(llvm::StringRef component)44774a628f7SDimitry Andric void FileSpec::AppendPathComponent(llvm::StringRef component) {
448f73363f1SDimitry Andric   llvm::SmallString<64> current_path;
449f73363f1SDimitry Andric   GetPath(current_path, false);
450f73363f1SDimitry Andric   llvm::sys::path::append(current_path, m_style, component);
45194994d37SDimitry Andric   SetFile(current_path, m_style);
45274a628f7SDimitry Andric }
45374a628f7SDimitry Andric 
AppendPathComponent(const FileSpec & new_path)45474a628f7SDimitry Andric void FileSpec::AppendPathComponent(const FileSpec &new_path) {
45574a628f7SDimitry Andric   return AppendPathComponent(new_path.GetPath(false));
45674a628f7SDimitry Andric }
45774a628f7SDimitry Andric 
RemoveLastPathComponent()458f73363f1SDimitry Andric bool FileSpec::RemoveLastPathComponent() {
459f73363f1SDimitry Andric   llvm::SmallString<64> current_path;
460f73363f1SDimitry Andric   GetPath(current_path, false);
461f73363f1SDimitry Andric   if (llvm::sys::path::has_parent_path(current_path, m_style)) {
46294994d37SDimitry Andric     SetFile(llvm::sys::path::parent_path(current_path, m_style));
463f73363f1SDimitry Andric     return true;
46474a628f7SDimitry Andric   }
465f73363f1SDimitry Andric   return false;
46674a628f7SDimitry Andric }
4677fa27ce4SDimitry Andric 
GetComponents() const4687fa27ce4SDimitry Andric std::vector<llvm::StringRef> FileSpec::GetComponents() const {
4697fa27ce4SDimitry Andric   std::vector<llvm::StringRef> components;
4707fa27ce4SDimitry Andric 
4717fa27ce4SDimitry Andric   auto dir_begin = llvm::sys::path::begin(m_directory.GetStringRef(), m_style);
4727fa27ce4SDimitry Andric   auto dir_end = llvm::sys::path::end(m_directory.GetStringRef());
4737fa27ce4SDimitry Andric 
4747fa27ce4SDimitry Andric   for (auto iter = dir_begin; iter != dir_end; ++iter) {
4757fa27ce4SDimitry Andric     if (*iter == "/" || *iter == ".")
4767fa27ce4SDimitry Andric       continue;
4777fa27ce4SDimitry Andric 
4787fa27ce4SDimitry Andric     components.push_back(*iter);
4797fa27ce4SDimitry Andric   }
4807fa27ce4SDimitry Andric 
4817fa27ce4SDimitry Andric   if (!m_filename.IsEmpty() && m_filename != "/" && m_filename != ".")
4827fa27ce4SDimitry Andric     components.push_back(m_filename.GetStringRef());
4837fa27ce4SDimitry Andric 
4847fa27ce4SDimitry Andric   return components;
4857fa27ce4SDimitry Andric }
4867fa27ce4SDimitry Andric 
48774a628f7SDimitry Andric /// Returns true if the filespec represents an implementation source
48874a628f7SDimitry Andric /// file (files with a ".c", ".cpp", ".m", ".mm" (many more)
48974a628f7SDimitry Andric /// extension).
49074a628f7SDimitry Andric ///
4915f29bb8aSDimitry Andric /// \return
49274a628f7SDimitry Andric ///     \b true if the filespec represents an implementation source
49374a628f7SDimitry Andric ///     file, \b false otherwise.
IsSourceImplementationFile() const49474a628f7SDimitry Andric bool FileSpec::IsSourceImplementationFile() const {
4957fa27ce4SDimitry Andric   llvm::StringRef extension = GetFileNameExtension();
4967fa27ce4SDimitry Andric   if (extension.empty())
49774a628f7SDimitry Andric     return false;
49874a628f7SDimitry Andric 
49974a628f7SDimitry Andric   static RegularExpression g_source_file_regex(llvm::StringRef(
500f73363f1SDimitry Andric       "^.([cC]|[mM]|[mM][mM]|[cC][pP][pP]|[cC]\\+\\+|[cC][xX][xX]|[cC][cC]|["
50174a628f7SDimitry Andric       "cC][pP]|[sS]|[aA][sS][mM]|[fF]|[fF]77|[fF]90|[fF]95|[fF]03|[fF][oO]["
50274a628f7SDimitry Andric       "rR]|[fF][tT][nN]|[fF][pP][pP]|[aA][dD][aA]|[aA][dD][bB]|[aA][dD][sS])"
50374a628f7SDimitry Andric       "$"));
5047fa27ce4SDimitry Andric   return g_source_file_regex.Execute(extension);
50574a628f7SDimitry Andric }
50674a628f7SDimitry Andric 
IsRelative() const50774a628f7SDimitry Andric bool FileSpec::IsRelative() const {
508f73363f1SDimitry Andric   return !IsAbsolute();
50974a628f7SDimitry Andric }
51074a628f7SDimitry Andric 
IsAbsolute() const511f73363f1SDimitry Andric bool FileSpec::IsAbsolute() const {
512e3b55780SDimitry Andric   // Check if we have cached if this path is absolute to avoid recalculating.
513e3b55780SDimitry Andric   if (m_absolute != Absolute::Calculate)
514e3b55780SDimitry Andric     return m_absolute == Absolute::Yes;
515f73363f1SDimitry Andric 
516e3b55780SDimitry Andric   m_absolute = Absolute::No;
517f73363f1SDimitry Andric 
518e3b55780SDimitry Andric   llvm::SmallString<64> path;
519e3b55780SDimitry Andric   GetPath(path, false);
520e3b55780SDimitry Andric 
521e3b55780SDimitry Andric   if (!path.empty()) {
522f73363f1SDimitry Andric     // We consider paths starting with ~ to be absolute.
523e3b55780SDimitry Andric     if (path[0] == '~' || llvm::sys::path::is_absolute(path, m_style))
524e3b55780SDimitry Andric       m_absolute = Absolute::Yes;
525e3b55780SDimitry Andric   }
526f73363f1SDimitry Andric 
527e3b55780SDimitry Andric   return m_absolute == Absolute::Yes;
528f73363f1SDimitry Andric }
52974a628f7SDimitry Andric 
MakeAbsolute(const FileSpec & dir)5305f29bb8aSDimitry Andric void FileSpec::MakeAbsolute(const FileSpec &dir) {
5315f29bb8aSDimitry Andric   if (IsRelative())
5325f29bb8aSDimitry Andric     PrependPathComponent(dir);
5335f29bb8aSDimitry Andric }
5345f29bb8aSDimitry Andric 
format(const FileSpec & F,raw_ostream & Stream,StringRef Style)53574a628f7SDimitry Andric void llvm::format_provider<FileSpec>::format(const FileSpec &F,
53674a628f7SDimitry Andric                                              raw_ostream &Stream,
53774a628f7SDimitry Andric                                              StringRef Style) {
538344a3780SDimitry Andric   assert((Style.empty() || Style.equals_insensitive("F") ||
539344a3780SDimitry Andric           Style.equals_insensitive("D")) &&
54074a628f7SDimitry Andric          "Invalid FileSpec style!");
54174a628f7SDimitry Andric 
54274a628f7SDimitry Andric   StringRef dir = F.GetDirectory().GetStringRef();
54374a628f7SDimitry Andric   StringRef file = F.GetFilename().GetStringRef();
54474a628f7SDimitry Andric 
54574a628f7SDimitry Andric   if (dir.empty() && file.empty()) {
54674a628f7SDimitry Andric     Stream << "(empty)";
54774a628f7SDimitry Andric     return;
54874a628f7SDimitry Andric   }
54974a628f7SDimitry Andric 
550344a3780SDimitry Andric   if (Style.equals_insensitive("F")) {
55174a628f7SDimitry Andric     Stream << (file.empty() ? "(empty)" : file);
55274a628f7SDimitry Andric     return;
55374a628f7SDimitry Andric   }
55474a628f7SDimitry Andric 
55574a628f7SDimitry Andric   // Style is either D or empty, either way we need to print the directory.
55674a628f7SDimitry Andric   if (!dir.empty()) {
557f73363f1SDimitry Andric     // Directory is stored in normalized form, which might be different than
558f73363f1SDimitry Andric     // preferred form.  In order to handle this, we need to cut off the
559f73363f1SDimitry Andric     // filename, then denormalize, then write the entire denorm'ed directory.
56074a628f7SDimitry Andric     llvm::SmallString<64> denormalized_dir = dir;
561f73363f1SDimitry Andric     Denormalize(denormalized_dir, F.GetPathStyle());
56274a628f7SDimitry Andric     Stream << denormalized_dir;
563f73363f1SDimitry Andric     Stream << GetPreferredPathSeparator(F.GetPathStyle());
56474a628f7SDimitry Andric   }
56574a628f7SDimitry Andric 
566344a3780SDimitry Andric   if (Style.equals_insensitive("D")) {
56774a628f7SDimitry Andric     // We only want to print the directory, so now just exit.
56874a628f7SDimitry Andric     if (dir.empty())
56974a628f7SDimitry Andric       Stream << "(empty)";
57074a628f7SDimitry Andric     return;
57174a628f7SDimitry Andric   }
57274a628f7SDimitry Andric 
57374a628f7SDimitry Andric   if (!file.empty())
57474a628f7SDimitry Andric     Stream << file;
57574a628f7SDimitry Andric }
576