1f678e45dSDimitry Andric //===-- FileSpec.cpp --------------------------------------------*- C++ -*-===//
2f678e45dSDimitry Andric //
3f678e45dSDimitry Andric // The LLVM Compiler Infrastructure
4f678e45dSDimitry Andric //
5f678e45dSDimitry Andric // This file is distributed under the University of Illinois Open Source
6f678e45dSDimitry Andric // License. See LICENSE.TXT for details.
7f678e45dSDimitry Andric //
8f678e45dSDimitry Andric //===----------------------------------------------------------------------===//
9f678e45dSDimitry Andric
10f678e45dSDimitry Andric #include "lldb/Utility/FileSpec.h"
11f678e45dSDimitry Andric #include "lldb/Utility/RegularExpression.h"
12f678e45dSDimitry Andric #include "lldb/Utility/Stream.h"
13f678e45dSDimitry Andric
144ba319b5SDimitry Andric #include "llvm/ADT/SmallString.h"
154ba319b5SDimitry Andric #include "llvm/ADT/SmallVector.h"
16f678e45dSDimitry Andric #include "llvm/ADT/StringRef.h"
174ba319b5SDimitry Andric #include "llvm/ADT/Triple.h"
184ba319b5SDimitry Andric #include "llvm/ADT/Twine.h"
194ba319b5SDimitry Andric #include "llvm/Support/ErrorOr.h"
20f678e45dSDimitry Andric #include "llvm/Support/FileSystem.h"
21f678e45dSDimitry Andric #include "llvm/Support/Program.h"
224ba319b5SDimitry Andric #include "llvm/Support/raw_ostream.h"
23f678e45dSDimitry Andric
24*b5893f02SDimitry Andric #include <algorithm>
25*b5893f02SDimitry Andric #include <system_error>
26*b5893f02SDimitry Andric #include <vector>
27f678e45dSDimitry Andric
28*b5893f02SDimitry Andric #include <assert.h>
29*b5893f02SDimitry Andric #include <limits.h>
30*b5893f02SDimitry Andric #include <stdio.h>
31*b5893f02SDimitry Andric #include <string.h>
32f678e45dSDimitry Andric
33f678e45dSDimitry Andric using namespace lldb;
34f678e45dSDimitry Andric using namespace lldb_private;
35f678e45dSDimitry Andric
36f678e45dSDimitry Andric namespace {
37f678e45dSDimitry Andric
GetNativeStyle()384ba319b5SDimitry Andric static constexpr FileSpec::Style GetNativeStyle() {
394ba319b5SDimitry Andric #if defined(_WIN32)
404ba319b5SDimitry Andric return FileSpec::Style::windows;
41f678e45dSDimitry Andric #else
424ba319b5SDimitry Andric return FileSpec::Style::posix;
43f678e45dSDimitry Andric #endif
44f678e45dSDimitry Andric }
45f678e45dSDimitry Andric
PathStyleIsPosix(FileSpec::Style style)464ba319b5SDimitry Andric bool PathStyleIsPosix(FileSpec::Style style) {
474ba319b5SDimitry Andric return (style == FileSpec::Style::posix ||
484ba319b5SDimitry Andric (style == FileSpec::Style::native &&
494ba319b5SDimitry Andric GetNativeStyle() == FileSpec::Style::posix));
50f678e45dSDimitry Andric }
51f678e45dSDimitry Andric
GetPathSeparators(FileSpec::Style style)524ba319b5SDimitry Andric const char *GetPathSeparators(FileSpec::Style style) {
534ba319b5SDimitry Andric return llvm::sys::path::get_separator(style).data();
54f678e45dSDimitry Andric }
55f678e45dSDimitry Andric
GetPreferredPathSeparator(FileSpec::Style style)564ba319b5SDimitry Andric char GetPreferredPathSeparator(FileSpec::Style style) {
574ba319b5SDimitry Andric return GetPathSeparators(style)[0];
58f678e45dSDimitry Andric }
59f678e45dSDimitry Andric
Denormalize(llvm::SmallVectorImpl<char> & path,FileSpec::Style style)604ba319b5SDimitry Andric void Denormalize(llvm::SmallVectorImpl<char> &path, FileSpec::Style style) {
614ba319b5SDimitry Andric if (PathStyleIsPosix(style))
62f678e45dSDimitry Andric return;
63f678e45dSDimitry Andric
64f678e45dSDimitry Andric std::replace(path.begin(), path.end(), '/', '\\');
65f678e45dSDimitry Andric }
66f678e45dSDimitry Andric
67f678e45dSDimitry Andric } // end anonymous namespace
68f678e45dSDimitry Andric
FileSpec()694ba319b5SDimitry Andric FileSpec::FileSpec() : m_style(GetNativeStyle()) {}
70f678e45dSDimitry Andric
71f678e45dSDimitry Andric //------------------------------------------------------------------
724ba319b5SDimitry Andric // Default constructor that can take an optional full path to a file on disk.
73f678e45dSDimitry Andric //------------------------------------------------------------------
FileSpec(llvm::StringRef path,Style style)74*b5893f02SDimitry Andric FileSpec::FileSpec(llvm::StringRef path, Style style) : m_style(style) {
75*b5893f02SDimitry Andric SetFile(path, style);
76f678e45dSDimitry Andric }
77f678e45dSDimitry Andric
FileSpec(llvm::StringRef path,const llvm::Triple & Triple)78*b5893f02SDimitry Andric FileSpec::FileSpec(llvm::StringRef path, const llvm::Triple &Triple)
79*b5893f02SDimitry Andric : FileSpec{path, Triple.isOSWindows() ? Style::windows : Style::posix} {}
80f678e45dSDimitry Andric
81f678e45dSDimitry Andric //------------------------------------------------------------------
82f678e45dSDimitry Andric // Copy constructor
83f678e45dSDimitry Andric //------------------------------------------------------------------
FileSpec(const FileSpec & rhs)84f678e45dSDimitry Andric FileSpec::FileSpec(const FileSpec &rhs)
85f678e45dSDimitry Andric : m_directory(rhs.m_directory), m_filename(rhs.m_filename),
864ba319b5SDimitry Andric m_is_resolved(rhs.m_is_resolved), m_style(rhs.m_style) {}
87f678e45dSDimitry Andric
88f678e45dSDimitry Andric //------------------------------------------------------------------
89f678e45dSDimitry Andric // Copy constructor
90f678e45dSDimitry Andric //------------------------------------------------------------------
FileSpec(const FileSpec * rhs)91f678e45dSDimitry Andric FileSpec::FileSpec(const FileSpec *rhs) : m_directory(), m_filename() {
92f678e45dSDimitry Andric if (rhs)
93f678e45dSDimitry Andric *this = *rhs;
94f678e45dSDimitry Andric }
95f678e45dSDimitry Andric
96f678e45dSDimitry Andric //------------------------------------------------------------------
97f678e45dSDimitry Andric // Virtual destructor in case anyone inherits from this class.
98f678e45dSDimitry Andric //------------------------------------------------------------------
~FileSpec()99f678e45dSDimitry Andric FileSpec::~FileSpec() {}
100f678e45dSDimitry Andric
1014ba319b5SDimitry Andric namespace {
1024ba319b5SDimitry Andric //------------------------------------------------------------------
1034ba319b5SDimitry Andric /// Safely get a character at the specified index.
1044ba319b5SDimitry Andric ///
1054ba319b5SDimitry Andric /// @param[in] path
1064ba319b5SDimitry Andric /// A full, partial, or relative path to a file.
1074ba319b5SDimitry Andric ///
1084ba319b5SDimitry Andric /// @param[in] i
1094ba319b5SDimitry Andric /// An index into path which may or may not be valid.
1104ba319b5SDimitry Andric ///
1114ba319b5SDimitry Andric /// @return
1124ba319b5SDimitry Andric /// The character at index \a i if the index is valid, or 0 if
1134ba319b5SDimitry Andric /// the index is not valid.
1144ba319b5SDimitry Andric //------------------------------------------------------------------
safeCharAtIndex(const llvm::StringRef & path,size_t i)1154ba319b5SDimitry Andric inline char safeCharAtIndex(const llvm::StringRef &path, size_t i) {
1164ba319b5SDimitry Andric if (i < path.size())
1174ba319b5SDimitry Andric return path[i];
1184ba319b5SDimitry Andric return 0;
1194ba319b5SDimitry Andric }
1204ba319b5SDimitry Andric
1214ba319b5SDimitry Andric //------------------------------------------------------------------
1224ba319b5SDimitry Andric /// Check if a path needs to be normalized.
1234ba319b5SDimitry Andric ///
1244ba319b5SDimitry Andric /// Check if a path needs to be normalized. We currently consider a
1254ba319b5SDimitry Andric /// path to need normalization if any of the following are true
1264ba319b5SDimitry Andric /// - path contains "/./"
1274ba319b5SDimitry Andric /// - path contains "/../"
1284ba319b5SDimitry Andric /// - path contains "//"
1294ba319b5SDimitry Andric /// - path ends with "/"
1304ba319b5SDimitry Andric /// Paths that start with "./" or with "../" are not considered to
1314ba319b5SDimitry Andric /// need normalization since we aren't trying to resolve the path,
1324ba319b5SDimitry Andric /// we are just trying to remove redundant things from the path.
1334ba319b5SDimitry Andric ///
1344ba319b5SDimitry Andric /// @param[in] path
1354ba319b5SDimitry Andric /// A full, partial, or relative path to a file.
1364ba319b5SDimitry Andric ///
1374ba319b5SDimitry Andric /// @return
1384ba319b5SDimitry Andric /// Returns \b true if the path needs to be normalized.
1394ba319b5SDimitry Andric //------------------------------------------------------------------
needsNormalization(const llvm::StringRef & path)1404ba319b5SDimitry Andric bool needsNormalization(const llvm::StringRef &path) {
1414ba319b5SDimitry Andric if (path.empty())
1424ba319b5SDimitry Andric return false;
1434ba319b5SDimitry Andric // We strip off leading "." values so these paths need to be normalized
1444ba319b5SDimitry Andric if (path[0] == '.')
1454ba319b5SDimitry Andric return true;
1464ba319b5SDimitry Andric for (auto i = path.find_first_of("\\/"); i != llvm::StringRef::npos;
1474ba319b5SDimitry Andric i = path.find_first_of("\\/", i + 1)) {
1484ba319b5SDimitry Andric const auto next = safeCharAtIndex(path, i+1);
1494ba319b5SDimitry Andric switch (next) {
1504ba319b5SDimitry Andric case 0:
1514ba319b5SDimitry Andric // path separator char at the end of the string which should be
1524ba319b5SDimitry Andric // stripped unless it is the one and only character
1534ba319b5SDimitry Andric return i > 0;
1544ba319b5SDimitry Andric case '/':
1554ba319b5SDimitry Andric case '\\':
1564ba319b5SDimitry Andric // two path separator chars in the middle of a path needs to be
1574ba319b5SDimitry Andric // normalized
1584ba319b5SDimitry Andric if (i > 0)
1594ba319b5SDimitry Andric return true;
1604ba319b5SDimitry Andric ++i;
1614ba319b5SDimitry Andric break;
1624ba319b5SDimitry Andric
1634ba319b5SDimitry Andric case '.': {
1644ba319b5SDimitry Andric const auto next_next = safeCharAtIndex(path, i+2);
1654ba319b5SDimitry Andric switch (next_next) {
1664ba319b5SDimitry Andric default: break;
1674ba319b5SDimitry Andric case 0: return true; // ends with "/."
1684ba319b5SDimitry Andric case '/':
1694ba319b5SDimitry Andric case '\\':
1704ba319b5SDimitry Andric return true; // contains "/./"
1714ba319b5SDimitry Andric case '.': {
1724ba319b5SDimitry Andric const auto next_next_next = safeCharAtIndex(path, i+3);
1734ba319b5SDimitry Andric switch (next_next_next) {
1744ba319b5SDimitry Andric default: break;
1754ba319b5SDimitry Andric case 0: return true; // ends with "/.."
1764ba319b5SDimitry Andric case '/':
1774ba319b5SDimitry Andric case '\\':
1784ba319b5SDimitry Andric return true; // contains "/../"
1794ba319b5SDimitry Andric }
1804ba319b5SDimitry Andric break;
1814ba319b5SDimitry Andric }
1824ba319b5SDimitry Andric }
1834ba319b5SDimitry Andric }
1844ba319b5SDimitry Andric break;
1854ba319b5SDimitry Andric
1864ba319b5SDimitry Andric default:
1874ba319b5SDimitry Andric break;
1884ba319b5SDimitry Andric }
1894ba319b5SDimitry Andric }
1904ba319b5SDimitry Andric return false;
1914ba319b5SDimitry Andric }
1924ba319b5SDimitry Andric
1934ba319b5SDimitry Andric
1944ba319b5SDimitry Andric }
195f678e45dSDimitry Andric //------------------------------------------------------------------
196f678e45dSDimitry Andric // Assignment operator.
197f678e45dSDimitry Andric //------------------------------------------------------------------
operator =(const FileSpec & rhs)198f678e45dSDimitry Andric const FileSpec &FileSpec::operator=(const FileSpec &rhs) {
199f678e45dSDimitry Andric if (this != &rhs) {
200f678e45dSDimitry Andric m_directory = rhs.m_directory;
201f678e45dSDimitry Andric m_filename = rhs.m_filename;
202f678e45dSDimitry Andric m_is_resolved = rhs.m_is_resolved;
2034ba319b5SDimitry Andric m_style = rhs.m_style;
204f678e45dSDimitry Andric }
205f678e45dSDimitry Andric return *this;
206f678e45dSDimitry Andric }
207f678e45dSDimitry Andric
SetFile(llvm::StringRef pathname)208*b5893f02SDimitry Andric void FileSpec::SetFile(llvm::StringRef pathname) { SetFile(pathname, m_style); }
2094ba319b5SDimitry Andric
210f678e45dSDimitry Andric //------------------------------------------------------------------
2114ba319b5SDimitry Andric // Update the contents of this object with a new path. The path will be split
2124ba319b5SDimitry Andric // up into a directory and filename and stored as uniqued string values for
2134ba319b5SDimitry Andric // quick comparison and efficient memory usage.
214f678e45dSDimitry Andric //------------------------------------------------------------------
SetFile(llvm::StringRef pathname,Style style)215*b5893f02SDimitry Andric void FileSpec::SetFile(llvm::StringRef pathname, Style style) {
216f678e45dSDimitry Andric m_filename.Clear();
217f678e45dSDimitry Andric m_directory.Clear();
218f678e45dSDimitry Andric m_is_resolved = false;
2194ba319b5SDimitry Andric m_style = (style == Style::native) ? GetNativeStyle() : style;
220f678e45dSDimitry Andric
221f678e45dSDimitry Andric if (pathname.empty())
222f678e45dSDimitry Andric return;
223f678e45dSDimitry Andric
224*b5893f02SDimitry Andric llvm::SmallString<128> resolved(pathname);
225f678e45dSDimitry Andric
2264ba319b5SDimitry Andric // Normalize the path by removing ".", ".." and other redundant components.
2274ba319b5SDimitry Andric if (needsNormalization(resolved))
2284ba319b5SDimitry Andric llvm::sys::path::remove_dots(resolved, true, m_style);
229f678e45dSDimitry Andric
2304ba319b5SDimitry Andric // Normalize back slashes to forward slashes
2314ba319b5SDimitry Andric if (m_style == Style::windows)
2324ba319b5SDimitry Andric std::replace(resolved.begin(), resolved.end(), '\\', '/');
2334ba319b5SDimitry Andric
2344ba319b5SDimitry Andric if (resolved.empty()) {
2354ba319b5SDimitry Andric // If we have no path after normalization set the path to the current
2364ba319b5SDimitry Andric // directory. This matches what python does and also a few other path
2374ba319b5SDimitry Andric // utilities.
2384ba319b5SDimitry Andric m_filename.SetString(".");
239f678e45dSDimitry Andric return;
240f678e45dSDimitry Andric }
241f678e45dSDimitry Andric
2424ba319b5SDimitry Andric // Split path into filename and directory. We rely on the underlying char
2434ba319b5SDimitry Andric // pointer to be nullptr when the components are empty.
2444ba319b5SDimitry Andric llvm::StringRef filename = llvm::sys::path::filename(resolved, m_style);
2454ba319b5SDimitry Andric if(!filename.empty())
2464ba319b5SDimitry Andric m_filename.SetString(filename);
247*b5893f02SDimitry Andric
2484ba319b5SDimitry Andric llvm::StringRef directory = llvm::sys::path::parent_path(resolved, m_style);
2494ba319b5SDimitry Andric if(!directory.empty())
2504ba319b5SDimitry Andric m_directory.SetString(directory);
251f678e45dSDimitry Andric }
252f678e45dSDimitry Andric
SetFile(llvm::StringRef path,const llvm::Triple & Triple)253*b5893f02SDimitry Andric void FileSpec::SetFile(llvm::StringRef path, const llvm::Triple &Triple) {
254*b5893f02SDimitry Andric return SetFile(path, Triple.isOSWindows() ? Style::windows : Style::posix);
255f678e45dSDimitry Andric }
256f678e45dSDimitry Andric
257f678e45dSDimitry Andric //----------------------------------------------------------------------
2584ba319b5SDimitry Andric // Convert to pointer operator. This allows code to check any FileSpec objects
2594ba319b5SDimitry Andric // to see if they contain anything valid using code such as:
260f678e45dSDimitry Andric //
261f678e45dSDimitry Andric // if (file_spec)
262f678e45dSDimitry Andric // {}
263f678e45dSDimitry Andric //----------------------------------------------------------------------
operator bool() const264f678e45dSDimitry Andric FileSpec::operator bool() const { return m_filename || m_directory; }
265f678e45dSDimitry Andric
266f678e45dSDimitry Andric //----------------------------------------------------------------------
2674ba319b5SDimitry Andric // Logical NOT operator. This allows code to check any FileSpec objects to see
2684ba319b5SDimitry Andric // if they are invalid using code such as:
269f678e45dSDimitry Andric //
270f678e45dSDimitry Andric // if (!file_spec)
271f678e45dSDimitry Andric // {}
272f678e45dSDimitry Andric //----------------------------------------------------------------------
operator !() const273f678e45dSDimitry Andric bool FileSpec::operator!() const { return !m_directory && !m_filename; }
274f678e45dSDimitry Andric
DirectoryEquals(const FileSpec & rhs) const275f678e45dSDimitry Andric bool FileSpec::DirectoryEquals(const FileSpec &rhs) const {
276f678e45dSDimitry Andric const bool case_sensitive = IsCaseSensitive() || rhs.IsCaseSensitive();
277f678e45dSDimitry Andric return ConstString::Equals(m_directory, rhs.m_directory, case_sensitive);
278f678e45dSDimitry Andric }
279f678e45dSDimitry Andric
FileEquals(const FileSpec & rhs) const280f678e45dSDimitry Andric bool FileSpec::FileEquals(const FileSpec &rhs) const {
281f678e45dSDimitry Andric const bool case_sensitive = IsCaseSensitive() || rhs.IsCaseSensitive();
282f678e45dSDimitry Andric return ConstString::Equals(m_filename, rhs.m_filename, case_sensitive);
283f678e45dSDimitry Andric }
284f678e45dSDimitry Andric
285f678e45dSDimitry Andric //------------------------------------------------------------------
286f678e45dSDimitry Andric // Equal to operator
287f678e45dSDimitry Andric //------------------------------------------------------------------
operator ==(const FileSpec & rhs) const288f678e45dSDimitry Andric bool FileSpec::operator==(const FileSpec &rhs) const {
289*b5893f02SDimitry Andric return FileEquals(rhs) && DirectoryEquals(rhs);
290f678e45dSDimitry Andric }
291f678e45dSDimitry Andric
292f678e45dSDimitry Andric //------------------------------------------------------------------
293f678e45dSDimitry Andric // Not equal to operator
294f678e45dSDimitry Andric //------------------------------------------------------------------
operator !=(const FileSpec & rhs) const295f678e45dSDimitry Andric bool FileSpec::operator!=(const FileSpec &rhs) const { return !(*this == rhs); }
296f678e45dSDimitry Andric
297f678e45dSDimitry Andric //------------------------------------------------------------------
298f678e45dSDimitry Andric // Less than operator
299f678e45dSDimitry Andric //------------------------------------------------------------------
operator <(const FileSpec & rhs) const300f678e45dSDimitry Andric bool FileSpec::operator<(const FileSpec &rhs) const {
301f678e45dSDimitry Andric return FileSpec::Compare(*this, rhs, true) < 0;
302f678e45dSDimitry Andric }
303f678e45dSDimitry Andric
304f678e45dSDimitry Andric //------------------------------------------------------------------
305f678e45dSDimitry Andric // Dump a FileSpec object to a stream
306f678e45dSDimitry Andric //------------------------------------------------------------------
operator <<(Stream & s,const FileSpec & f)307f678e45dSDimitry Andric Stream &lldb_private::operator<<(Stream &s, const FileSpec &f) {
308f678e45dSDimitry Andric f.Dump(&s);
309f678e45dSDimitry Andric return s;
310f678e45dSDimitry Andric }
311f678e45dSDimitry Andric
312f678e45dSDimitry Andric //------------------------------------------------------------------
3134ba319b5SDimitry Andric // Clear this object by releasing both the directory and filename string values
3144ba319b5SDimitry Andric // and making them both the empty string.
315f678e45dSDimitry Andric //------------------------------------------------------------------
Clear()316f678e45dSDimitry Andric void FileSpec::Clear() {
317f678e45dSDimitry Andric m_directory.Clear();
318f678e45dSDimitry Andric m_filename.Clear();
319f678e45dSDimitry Andric }
320f678e45dSDimitry Andric
321f678e45dSDimitry Andric //------------------------------------------------------------------
3224ba319b5SDimitry Andric // Compare two FileSpec objects. If "full" is true, then both the directory and
3234ba319b5SDimitry Andric // the filename must match. If "full" is false, then the directory names for
3244ba319b5SDimitry Andric // "a" and "b" are only compared if they are both non-empty. This allows a
3254ba319b5SDimitry Andric // FileSpec object to only contain a filename and it can match FileSpec objects
3264ba319b5SDimitry Andric // that have matching filenames with different paths.
327f678e45dSDimitry Andric //
3284ba319b5SDimitry Andric // Return -1 if the "a" is less than "b", 0 if "a" is equal to "b" and "1" if
3294ba319b5SDimitry Andric // "a" is greater than "b".
330f678e45dSDimitry Andric //------------------------------------------------------------------
Compare(const FileSpec & a,const FileSpec & b,bool full)331f678e45dSDimitry Andric int FileSpec::Compare(const FileSpec &a, const FileSpec &b, bool full) {
332f678e45dSDimitry Andric int result = 0;
333f678e45dSDimitry Andric
334f678e45dSDimitry Andric // case sensitivity of compare
335f678e45dSDimitry Andric const bool case_sensitive = a.IsCaseSensitive() || b.IsCaseSensitive();
336f678e45dSDimitry Andric
337f678e45dSDimitry Andric // If full is true, then we must compare both the directory and filename.
338f678e45dSDimitry Andric
3394ba319b5SDimitry Andric // If full is false, then if either directory is empty, then we match on the
3404ba319b5SDimitry Andric // basename only, and if both directories have valid values, we still do a
3414ba319b5SDimitry Andric // full compare. This allows for matching when we just have a filename in one
3424ba319b5SDimitry Andric // of the FileSpec objects.
343f678e45dSDimitry Andric
344f678e45dSDimitry Andric if (full || (a.m_directory && b.m_directory)) {
345f678e45dSDimitry Andric result = ConstString::Compare(a.m_directory, b.m_directory, case_sensitive);
346f678e45dSDimitry Andric if (result)
347f678e45dSDimitry Andric return result;
348f678e45dSDimitry Andric }
349f678e45dSDimitry Andric return ConstString::Compare(a.m_filename, b.m_filename, case_sensitive);
350f678e45dSDimitry Andric }
351f678e45dSDimitry Andric
Equal(const FileSpec & a,const FileSpec & b,bool full)3524ba319b5SDimitry Andric bool FileSpec::Equal(const FileSpec &a, const FileSpec &b, bool full) {
353f678e45dSDimitry Andric // case sensitivity of equality test
354f678e45dSDimitry Andric const bool case_sensitive = a.IsCaseSensitive() || b.IsCaseSensitive();
355f678e45dSDimitry Andric
3564ba319b5SDimitry Andric const bool filenames_equal = ConstString::Equals(a.m_filename,
357f678e45dSDimitry Andric b.m_filename,
358f678e45dSDimitry Andric case_sensitive);
359f678e45dSDimitry Andric
3604ba319b5SDimitry Andric if (!filenames_equal)
361f678e45dSDimitry Andric return false;
362f678e45dSDimitry Andric
363f678e45dSDimitry Andric if (!full && (a.GetDirectory().IsEmpty() || b.GetDirectory().IsEmpty()))
364f678e45dSDimitry Andric return filenames_equal;
365f678e45dSDimitry Andric
366f678e45dSDimitry Andric return a == b;
367f678e45dSDimitry Andric }
368f678e45dSDimitry Andric
369f678e45dSDimitry Andric //------------------------------------------------------------------
3704ba319b5SDimitry Andric // Dump the object to the supplied stream. If the object contains a valid
3714ba319b5SDimitry Andric // directory name, it will be displayed followed by a directory delimiter, and
3724ba319b5SDimitry Andric // the filename.
373f678e45dSDimitry Andric //------------------------------------------------------------------
Dump(Stream * s) const374f678e45dSDimitry Andric void FileSpec::Dump(Stream *s) const {
375f678e45dSDimitry Andric if (s) {
376f678e45dSDimitry Andric std::string path{GetPath(true)};
377f678e45dSDimitry Andric s->PutCString(path);
3784ba319b5SDimitry Andric char path_separator = GetPreferredPathSeparator(m_style);
379f678e45dSDimitry Andric if (!m_filename && !path.empty() && path.back() != path_separator)
380f678e45dSDimitry Andric s->PutChar(path_separator);
381f678e45dSDimitry Andric }
382f678e45dSDimitry Andric }
383f678e45dSDimitry Andric
GetPathStyle() const3844ba319b5SDimitry Andric FileSpec::Style FileSpec::GetPathStyle() const { return m_style; }
385f678e45dSDimitry Andric
386f678e45dSDimitry Andric //------------------------------------------------------------------
387f678e45dSDimitry Andric // Directory string get accessor.
388f678e45dSDimitry Andric //------------------------------------------------------------------
GetDirectory()389f678e45dSDimitry Andric ConstString &FileSpec::GetDirectory() { return m_directory; }
390f678e45dSDimitry Andric
391f678e45dSDimitry Andric //------------------------------------------------------------------
392f678e45dSDimitry Andric // Directory string const get accessor.
393f678e45dSDimitry Andric //------------------------------------------------------------------
GetDirectory() const394f678e45dSDimitry Andric const ConstString &FileSpec::GetDirectory() const { return m_directory; }
395f678e45dSDimitry Andric
396f678e45dSDimitry Andric //------------------------------------------------------------------
397f678e45dSDimitry Andric // Filename string get accessor.
398f678e45dSDimitry Andric //------------------------------------------------------------------
GetFilename()399f678e45dSDimitry Andric ConstString &FileSpec::GetFilename() { return m_filename; }
400f678e45dSDimitry Andric
401f678e45dSDimitry Andric //------------------------------------------------------------------
402f678e45dSDimitry Andric // Filename string const get accessor.
403f678e45dSDimitry Andric //------------------------------------------------------------------
GetFilename() const404f678e45dSDimitry Andric const ConstString &FileSpec::GetFilename() const { return m_filename; }
405f678e45dSDimitry Andric
406f678e45dSDimitry Andric //------------------------------------------------------------------
4074ba319b5SDimitry Andric // Extract the directory and path into a fixed buffer. This is needed as the
4084ba319b5SDimitry Andric // directory and path are stored in separate string values.
409f678e45dSDimitry Andric //------------------------------------------------------------------
GetPath(char * path,size_t path_max_len,bool denormalize) const410f678e45dSDimitry Andric size_t FileSpec::GetPath(char *path, size_t path_max_len,
411f678e45dSDimitry Andric bool denormalize) const {
412f678e45dSDimitry Andric if (!path)
413f678e45dSDimitry Andric return 0;
414f678e45dSDimitry Andric
415f678e45dSDimitry Andric std::string result = GetPath(denormalize);
416f678e45dSDimitry Andric ::snprintf(path, path_max_len, "%s", result.c_str());
417f678e45dSDimitry Andric return std::min(path_max_len - 1, result.length());
418f678e45dSDimitry Andric }
419f678e45dSDimitry Andric
GetPath(bool denormalize) const420f678e45dSDimitry Andric std::string FileSpec::GetPath(bool denormalize) const {
421f678e45dSDimitry Andric llvm::SmallString<64> result;
422f678e45dSDimitry Andric GetPath(result, denormalize);
423f678e45dSDimitry Andric return std::string(result.begin(), result.end());
424f678e45dSDimitry Andric }
425f678e45dSDimitry Andric
GetCString(bool denormalize) const426f678e45dSDimitry Andric const char *FileSpec::GetCString(bool denormalize) const {
427*b5893f02SDimitry Andric return ConstString{GetPath(denormalize)}.AsCString(nullptr);
428f678e45dSDimitry Andric }
429f678e45dSDimitry Andric
GetPath(llvm::SmallVectorImpl<char> & path,bool denormalize) const430f678e45dSDimitry Andric void FileSpec::GetPath(llvm::SmallVectorImpl<char> &path,
431f678e45dSDimitry Andric bool denormalize) const {
432f678e45dSDimitry Andric path.append(m_directory.GetStringRef().begin(),
433f678e45dSDimitry Andric m_directory.GetStringRef().end());
4344ba319b5SDimitry Andric // Since the path was normalized and all paths use '/' when stored in these
4354ba319b5SDimitry Andric // objects, we don't need to look for the actual syntax specific path
4364ba319b5SDimitry Andric // separator, we just look for and insert '/'.
4374ba319b5SDimitry Andric if (m_directory && m_filename && m_directory.GetStringRef().back() != '/' &&
4384ba319b5SDimitry Andric m_filename.GetStringRef().back() != '/')
4394ba319b5SDimitry Andric path.insert(path.end(), '/');
440f678e45dSDimitry Andric path.append(m_filename.GetStringRef().begin(),
441f678e45dSDimitry Andric m_filename.GetStringRef().end());
442f678e45dSDimitry Andric if (denormalize && !path.empty())
4434ba319b5SDimitry Andric Denormalize(path, m_style);
444f678e45dSDimitry Andric }
445f678e45dSDimitry Andric
GetFileNameExtension() const446f678e45dSDimitry Andric ConstString FileSpec::GetFileNameExtension() const {
4474ba319b5SDimitry Andric return ConstString(
4484ba319b5SDimitry Andric llvm::sys::path::extension(m_filename.GetStringRef(), m_style));
449f678e45dSDimitry Andric }
450f678e45dSDimitry Andric
GetFileNameStrippingExtension() const451f678e45dSDimitry Andric ConstString FileSpec::GetFileNameStrippingExtension() const {
4524ba319b5SDimitry Andric return ConstString(llvm::sys::path::stem(m_filename.GetStringRef(), m_style));
453f678e45dSDimitry Andric }
454f678e45dSDimitry Andric
455f678e45dSDimitry Andric //------------------------------------------------------------------
4564ba319b5SDimitry Andric // Return the size in bytes that this object takes in memory. This returns the
4574ba319b5SDimitry Andric // size in bytes of this object, not any shared string values it may refer to.
458f678e45dSDimitry Andric //------------------------------------------------------------------
MemorySize() const459f678e45dSDimitry Andric size_t FileSpec::MemorySize() const {
460f678e45dSDimitry Andric return m_filename.MemorySize() + m_directory.MemorySize();
461f678e45dSDimitry Andric }
462f678e45dSDimitry Andric
463f678e45dSDimitry Andric FileSpec
CopyByAppendingPathComponent(llvm::StringRef component) const464f678e45dSDimitry Andric FileSpec::CopyByAppendingPathComponent(llvm::StringRef component) const {
465f678e45dSDimitry Andric FileSpec ret = *this;
466f678e45dSDimitry Andric ret.AppendPathComponent(component);
467f678e45dSDimitry Andric return ret;
468f678e45dSDimitry Andric }
469f678e45dSDimitry Andric
CopyByRemovingLastPathComponent() const470f678e45dSDimitry Andric FileSpec FileSpec::CopyByRemovingLastPathComponent() const {
4714ba319b5SDimitry Andric llvm::SmallString<64> current_path;
4724ba319b5SDimitry Andric GetPath(current_path, false);
4734ba319b5SDimitry Andric if (llvm::sys::path::has_parent_path(current_path, m_style))
474*b5893f02SDimitry Andric return FileSpec(llvm::sys::path::parent_path(current_path, m_style),
4754ba319b5SDimitry Andric m_style);
4764ba319b5SDimitry Andric return *this;
477f678e45dSDimitry Andric }
478f678e45dSDimitry Andric
GetLastPathComponent() const479f678e45dSDimitry Andric ConstString FileSpec::GetLastPathComponent() const {
4804ba319b5SDimitry Andric llvm::SmallString<64> current_path;
4814ba319b5SDimitry Andric GetPath(current_path, false);
4824ba319b5SDimitry Andric return ConstString(llvm::sys::path::filename(current_path, m_style));
483f678e45dSDimitry Andric }
484f678e45dSDimitry Andric
PrependPathComponent(llvm::StringRef component)485f678e45dSDimitry Andric void FileSpec::PrependPathComponent(llvm::StringRef component) {
4864ba319b5SDimitry Andric llvm::SmallString<64> new_path(component);
4874ba319b5SDimitry Andric llvm::SmallString<64> current_path;
4884ba319b5SDimitry Andric GetPath(current_path, false);
4894ba319b5SDimitry Andric llvm::sys::path::append(new_path,
4904ba319b5SDimitry Andric llvm::sys::path::begin(current_path, m_style),
4914ba319b5SDimitry Andric llvm::sys::path::end(current_path), m_style);
492*b5893f02SDimitry Andric SetFile(new_path, m_style);
493f678e45dSDimitry Andric }
494f678e45dSDimitry Andric
PrependPathComponent(const FileSpec & new_path)495f678e45dSDimitry Andric void FileSpec::PrependPathComponent(const FileSpec &new_path) {
496f678e45dSDimitry Andric return PrependPathComponent(new_path.GetPath(false));
497f678e45dSDimitry Andric }
498f678e45dSDimitry Andric
AppendPathComponent(llvm::StringRef component)499f678e45dSDimitry Andric void FileSpec::AppendPathComponent(llvm::StringRef component) {
5004ba319b5SDimitry Andric llvm::SmallString<64> current_path;
5014ba319b5SDimitry Andric GetPath(current_path, false);
5024ba319b5SDimitry Andric llvm::sys::path::append(current_path, m_style, component);
503*b5893f02SDimitry Andric SetFile(current_path, m_style);
504f678e45dSDimitry Andric }
505f678e45dSDimitry Andric
AppendPathComponent(const FileSpec & new_path)506f678e45dSDimitry Andric void FileSpec::AppendPathComponent(const FileSpec &new_path) {
507f678e45dSDimitry Andric return AppendPathComponent(new_path.GetPath(false));
508f678e45dSDimitry Andric }
509f678e45dSDimitry Andric
RemoveLastPathComponent()5104ba319b5SDimitry Andric bool FileSpec::RemoveLastPathComponent() {
5114ba319b5SDimitry Andric llvm::SmallString<64> current_path;
5124ba319b5SDimitry Andric GetPath(current_path, false);
5134ba319b5SDimitry Andric if (llvm::sys::path::has_parent_path(current_path, m_style)) {
514*b5893f02SDimitry Andric SetFile(llvm::sys::path::parent_path(current_path, m_style));
5154ba319b5SDimitry Andric return true;
516f678e45dSDimitry Andric }
5174ba319b5SDimitry Andric return false;
518f678e45dSDimitry Andric }
519f678e45dSDimitry Andric //------------------------------------------------------------------
520f678e45dSDimitry Andric /// Returns true if the filespec represents an implementation source
521f678e45dSDimitry Andric /// file (files with a ".c", ".cpp", ".m", ".mm" (many more)
522f678e45dSDimitry Andric /// extension).
523f678e45dSDimitry Andric ///
524f678e45dSDimitry Andric /// @return
525f678e45dSDimitry Andric /// \b true if the filespec represents an implementation source
526f678e45dSDimitry Andric /// file, \b false otherwise.
527f678e45dSDimitry Andric //------------------------------------------------------------------
IsSourceImplementationFile() const528f678e45dSDimitry Andric bool FileSpec::IsSourceImplementationFile() const {
529f678e45dSDimitry Andric ConstString extension(GetFileNameExtension());
530f678e45dSDimitry Andric if (!extension)
531f678e45dSDimitry Andric return false;
532f678e45dSDimitry Andric
533f678e45dSDimitry Andric static RegularExpression g_source_file_regex(llvm::StringRef(
5344ba319b5SDimitry Andric "^.([cC]|[mM]|[mM][mM]|[cC][pP][pP]|[cC]\\+\\+|[cC][xX][xX]|[cC][cC]|["
535f678e45dSDimitry Andric "cC][pP]|[sS]|[aA][sS][mM]|[fF]|[fF]77|[fF]90|[fF]95|[fF]03|[fF][oO]["
536f678e45dSDimitry Andric "rR]|[fF][tT][nN]|[fF][pP][pP]|[aA][dD][aA]|[aA][dD][bB]|[aA][dD][sS])"
537f678e45dSDimitry Andric "$"));
538f678e45dSDimitry Andric return g_source_file_regex.Execute(extension.GetStringRef());
539f678e45dSDimitry Andric }
540f678e45dSDimitry Andric
IsRelative() const541f678e45dSDimitry Andric bool FileSpec::IsRelative() const {
5424ba319b5SDimitry Andric return !IsAbsolute();
543f678e45dSDimitry Andric }
544f678e45dSDimitry Andric
IsAbsolute() const5454ba319b5SDimitry Andric bool FileSpec::IsAbsolute() const {
5464ba319b5SDimitry Andric llvm::SmallString<64> current_path;
5474ba319b5SDimitry Andric GetPath(current_path, false);
5484ba319b5SDimitry Andric
5494ba319b5SDimitry Andric // Early return if the path is empty.
5504ba319b5SDimitry Andric if (current_path.empty())
5514ba319b5SDimitry Andric return false;
5524ba319b5SDimitry Andric
5534ba319b5SDimitry Andric // We consider paths starting with ~ to be absolute.
5544ba319b5SDimitry Andric if (current_path[0] == '~')
5554ba319b5SDimitry Andric return true;
5564ba319b5SDimitry Andric
5574ba319b5SDimitry Andric return llvm::sys::path::is_absolute(current_path, m_style);
5584ba319b5SDimitry Andric }
559f678e45dSDimitry Andric
format(const FileSpec & F,raw_ostream & Stream,StringRef Style)560f678e45dSDimitry Andric void llvm::format_provider<FileSpec>::format(const FileSpec &F,
561f678e45dSDimitry Andric raw_ostream &Stream,
562f678e45dSDimitry Andric StringRef Style) {
563f678e45dSDimitry Andric assert(
564f678e45dSDimitry Andric (Style.empty() || Style.equals_lower("F") || Style.equals_lower("D")) &&
565f678e45dSDimitry Andric "Invalid FileSpec style!");
566f678e45dSDimitry Andric
567f678e45dSDimitry Andric StringRef dir = F.GetDirectory().GetStringRef();
568f678e45dSDimitry Andric StringRef file = F.GetFilename().GetStringRef();
569f678e45dSDimitry Andric
570f678e45dSDimitry Andric if (dir.empty() && file.empty()) {
571f678e45dSDimitry Andric Stream << "(empty)";
572f678e45dSDimitry Andric return;
573f678e45dSDimitry Andric }
574f678e45dSDimitry Andric
575f678e45dSDimitry Andric if (Style.equals_lower("F")) {
576f678e45dSDimitry Andric Stream << (file.empty() ? "(empty)" : file);
577f678e45dSDimitry Andric return;
578f678e45dSDimitry Andric }
579f678e45dSDimitry Andric
580f678e45dSDimitry Andric // Style is either D or empty, either way we need to print the directory.
581f678e45dSDimitry Andric if (!dir.empty()) {
5824ba319b5SDimitry Andric // Directory is stored in normalized form, which might be different than
5834ba319b5SDimitry Andric // preferred form. In order to handle this, we need to cut off the
5844ba319b5SDimitry Andric // filename, then denormalize, then write the entire denorm'ed directory.
585f678e45dSDimitry Andric llvm::SmallString<64> denormalized_dir = dir;
5864ba319b5SDimitry Andric Denormalize(denormalized_dir, F.GetPathStyle());
587f678e45dSDimitry Andric Stream << denormalized_dir;
5884ba319b5SDimitry Andric Stream << GetPreferredPathSeparator(F.GetPathStyle());
589f678e45dSDimitry Andric }
590f678e45dSDimitry Andric
591f678e45dSDimitry Andric if (Style.equals_lower("D")) {
592f678e45dSDimitry Andric // We only want to print the directory, so now just exit.
593f678e45dSDimitry Andric if (dir.empty())
594f678e45dSDimitry Andric Stream << "(empty)";
595f678e45dSDimitry Andric return;
596f678e45dSDimitry Andric }
597f678e45dSDimitry Andric
598f678e45dSDimitry Andric if (!file.empty())
599f678e45dSDimitry Andric Stream << file;
600f678e45dSDimitry Andric }
601