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"
150b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
160b57cec5SDimitry Andric #include "llvm/ADT/Triple.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"
220b57cec5SDimitry Andric 
230b57cec5SDimitry Andric #include <algorithm>
240b57cec5SDimitry Andric #include <system_error>
250b57cec5SDimitry Andric #include <vector>
260b57cec5SDimitry Andric 
27*5f7ddb14SDimitry Andric #include <cassert>
28*5f7ddb14SDimitry Andric #include <climits>
29*5f7ddb14SDimitry Andric #include <cstdio>
30*5f7ddb14SDimitry Andric #include <cstring>
310b57cec5SDimitry Andric 
320b57cec5SDimitry Andric using namespace lldb;
330b57cec5SDimitry Andric using namespace lldb_private;
340b57cec5SDimitry Andric 
350b57cec5SDimitry Andric namespace {
360b57cec5SDimitry Andric 
GetNativeStyle()370b57cec5SDimitry Andric static constexpr FileSpec::Style GetNativeStyle() {
380b57cec5SDimitry Andric #if defined(_WIN32)
390b57cec5SDimitry Andric   return FileSpec::Style::windows;
400b57cec5SDimitry Andric #else
410b57cec5SDimitry Andric   return FileSpec::Style::posix;
420b57cec5SDimitry Andric #endif
430b57cec5SDimitry Andric }
440b57cec5SDimitry Andric 
PathStyleIsPosix(FileSpec::Style style)450b57cec5SDimitry Andric bool PathStyleIsPosix(FileSpec::Style style) {
460b57cec5SDimitry Andric   return (style == FileSpec::Style::posix ||
470b57cec5SDimitry Andric           (style == FileSpec::Style::native &&
480b57cec5SDimitry Andric            GetNativeStyle() == FileSpec::Style::posix));
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)710b57cec5SDimitry Andric FileSpec::FileSpec(llvm::StringRef path, Style style) : m_style(style) {
720b57cec5SDimitry 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)1740b57cec5SDimitry Andric void FileSpec::SetFile(llvm::StringRef pathname, Style style) {
1750b57cec5SDimitry Andric   m_filename.Clear();
1760b57cec5SDimitry Andric   m_directory.Clear();
1770b57cec5SDimitry Andric   m_is_resolved = false;
1780b57cec5SDimitry Andric   m_style = (style == Style::native) ? GetNativeStyle() : style;
1790b57cec5SDimitry Andric 
1800b57cec5SDimitry Andric   if (pathname.empty())
1810b57cec5SDimitry Andric     return;
1820b57cec5SDimitry Andric 
1830b57cec5SDimitry Andric   llvm::SmallString<128> resolved(pathname);
1840b57cec5SDimitry Andric 
1850b57cec5SDimitry Andric   // Normalize the path by removing ".", ".." and other redundant components.
1860b57cec5SDimitry Andric   if (needsNormalization(resolved))
1870b57cec5SDimitry Andric     llvm::sys::path::remove_dots(resolved, true, m_style);
1880b57cec5SDimitry Andric 
1890b57cec5SDimitry Andric   // Normalize back slashes to forward slashes
1900b57cec5SDimitry Andric   if (m_style == Style::windows)
1910b57cec5SDimitry Andric     std::replace(resolved.begin(), resolved.end(), '\\', '/');
1920b57cec5SDimitry Andric 
1930b57cec5SDimitry Andric   if (resolved.empty()) {
1940b57cec5SDimitry Andric     // If we have no path after normalization set the path to the current
1950b57cec5SDimitry Andric     // directory. This matches what python does and also a few other path
1960b57cec5SDimitry Andric     // utilities.
1970b57cec5SDimitry Andric     m_filename.SetString(".");
1980b57cec5SDimitry Andric     return;
1990b57cec5SDimitry Andric   }
2000b57cec5SDimitry Andric 
2010b57cec5SDimitry Andric   // Split path into filename and directory. We rely on the underlying char
2020b57cec5SDimitry Andric   // pointer to be nullptr when the components are empty.
2030b57cec5SDimitry Andric   llvm::StringRef filename = llvm::sys::path::filename(resolved, m_style);
2040b57cec5SDimitry Andric   if(!filename.empty())
2050b57cec5SDimitry Andric     m_filename.SetString(filename);
2060b57cec5SDimitry Andric 
2070b57cec5SDimitry Andric   llvm::StringRef directory = llvm::sys::path::parent_path(resolved, m_style);
2080b57cec5SDimitry Andric   if(!directory.empty())
2090b57cec5SDimitry Andric     m_directory.SetString(directory);
2100b57cec5SDimitry Andric }
2110b57cec5SDimitry Andric 
SetFile(llvm::StringRef path,const llvm::Triple & triple)2129dba64beSDimitry Andric void FileSpec::SetFile(llvm::StringRef path, const llvm::Triple &triple) {
2139dba64beSDimitry Andric   return SetFile(path, triple.isOSWindows() ? Style::windows : Style::posix);
2140b57cec5SDimitry Andric }
2150b57cec5SDimitry Andric 
2160b57cec5SDimitry Andric // Convert to pointer operator. This allows code to check any FileSpec objects
2170b57cec5SDimitry Andric // to see if they contain anything valid using code such as:
2180b57cec5SDimitry Andric //
2190b57cec5SDimitry Andric //  if (file_spec)
2200b57cec5SDimitry Andric //  {}
operator bool() const2210b57cec5SDimitry Andric FileSpec::operator bool() const { return m_filename || m_directory; }
2220b57cec5SDimitry Andric 
2230b57cec5SDimitry Andric // Logical NOT operator. This allows code to check any FileSpec objects to see
2240b57cec5SDimitry Andric // if they are invalid using code such as:
2250b57cec5SDimitry Andric //
2260b57cec5SDimitry Andric //  if (!file_spec)
2270b57cec5SDimitry Andric //  {}
operator !() const2280b57cec5SDimitry Andric bool FileSpec::operator!() const { return !m_directory && !m_filename; }
2290b57cec5SDimitry Andric 
DirectoryEquals(const FileSpec & rhs) const2300b57cec5SDimitry Andric bool FileSpec::DirectoryEquals(const FileSpec &rhs) const {
2310b57cec5SDimitry Andric   const bool case_sensitive = IsCaseSensitive() || rhs.IsCaseSensitive();
2320b57cec5SDimitry Andric   return ConstString::Equals(m_directory, rhs.m_directory, case_sensitive);
2330b57cec5SDimitry Andric }
2340b57cec5SDimitry Andric 
FileEquals(const FileSpec & rhs) const2350b57cec5SDimitry Andric bool FileSpec::FileEquals(const FileSpec &rhs) const {
2360b57cec5SDimitry Andric   const bool case_sensitive = IsCaseSensitive() || rhs.IsCaseSensitive();
2370b57cec5SDimitry Andric   return ConstString::Equals(m_filename, rhs.m_filename, case_sensitive);
2380b57cec5SDimitry Andric }
2390b57cec5SDimitry Andric 
2400b57cec5SDimitry Andric // Equal to operator
operator ==(const FileSpec & rhs) const2410b57cec5SDimitry Andric bool FileSpec::operator==(const FileSpec &rhs) const {
2420b57cec5SDimitry Andric   return FileEquals(rhs) && DirectoryEquals(rhs);
2430b57cec5SDimitry Andric }
2440b57cec5SDimitry Andric 
2450b57cec5SDimitry Andric // Not equal to operator
operator !=(const FileSpec & rhs) const2460b57cec5SDimitry Andric bool FileSpec::operator!=(const FileSpec &rhs) const { return !(*this == rhs); }
2470b57cec5SDimitry Andric 
2480b57cec5SDimitry Andric // Less than operator
operator <(const FileSpec & rhs) const2490b57cec5SDimitry Andric bool FileSpec::operator<(const FileSpec &rhs) const {
2500b57cec5SDimitry Andric   return FileSpec::Compare(*this, rhs, true) < 0;
2510b57cec5SDimitry Andric }
2520b57cec5SDimitry Andric 
2530b57cec5SDimitry Andric // Dump a FileSpec object to a stream
operator <<(Stream & s,const FileSpec & f)2540b57cec5SDimitry Andric Stream &lldb_private::operator<<(Stream &s, const FileSpec &f) {
255480093f4SDimitry Andric   f.Dump(s.AsRawOstream());
2560b57cec5SDimitry Andric   return s;
2570b57cec5SDimitry Andric }
2580b57cec5SDimitry Andric 
2590b57cec5SDimitry Andric // Clear this object by releasing both the directory and filename string values
2600b57cec5SDimitry Andric // and making them both the empty string.
Clear()2610b57cec5SDimitry Andric void FileSpec::Clear() {
2620b57cec5SDimitry Andric   m_directory.Clear();
2630b57cec5SDimitry Andric   m_filename.Clear();
2640b57cec5SDimitry Andric }
2650b57cec5SDimitry Andric 
2660b57cec5SDimitry Andric // Compare two FileSpec objects. If "full" is true, then both the directory and
2670b57cec5SDimitry Andric // the filename must match. If "full" is false, then the directory names for
2680b57cec5SDimitry Andric // "a" and "b" are only compared if they are both non-empty. This allows a
2690b57cec5SDimitry Andric // FileSpec object to only contain a filename and it can match FileSpec objects
2700b57cec5SDimitry Andric // that have matching filenames with different paths.
2710b57cec5SDimitry Andric //
2720b57cec5SDimitry Andric // Return -1 if the "a" is less than "b", 0 if "a" is equal to "b" and "1" if
2730b57cec5SDimitry Andric // "a" is greater than "b".
Compare(const FileSpec & a,const FileSpec & b,bool full)2740b57cec5SDimitry Andric int FileSpec::Compare(const FileSpec &a, const FileSpec &b, bool full) {
2750b57cec5SDimitry Andric   int result = 0;
2760b57cec5SDimitry Andric 
2770b57cec5SDimitry Andric   // case sensitivity of compare
2780b57cec5SDimitry Andric   const bool case_sensitive = a.IsCaseSensitive() || b.IsCaseSensitive();
2790b57cec5SDimitry Andric 
2800b57cec5SDimitry Andric   // If full is true, then we must compare both the directory and filename.
2810b57cec5SDimitry Andric 
2820b57cec5SDimitry Andric   // If full is false, then if either directory is empty, then we match on the
2830b57cec5SDimitry Andric   // basename only, and if both directories have valid values, we still do a
2840b57cec5SDimitry Andric   // full compare. This allows for matching when we just have a filename in one
2850b57cec5SDimitry Andric   // of the FileSpec objects.
2860b57cec5SDimitry Andric 
2870b57cec5SDimitry Andric   if (full || (a.m_directory && b.m_directory)) {
2880b57cec5SDimitry Andric     result = ConstString::Compare(a.m_directory, b.m_directory, case_sensitive);
2890b57cec5SDimitry Andric     if (result)
2900b57cec5SDimitry Andric       return result;
2910b57cec5SDimitry Andric   }
2920b57cec5SDimitry Andric   return ConstString::Compare(a.m_filename, b.m_filename, case_sensitive);
2930b57cec5SDimitry Andric }
2940b57cec5SDimitry Andric 
Equal(const FileSpec & a,const FileSpec & b,bool full)2950b57cec5SDimitry Andric bool FileSpec::Equal(const FileSpec &a, const FileSpec &b, bool full) {
296480093f4SDimitry Andric   if (full || (a.GetDirectory() && b.GetDirectory()))
2970b57cec5SDimitry Andric     return a == b;
298480093f4SDimitry Andric 
299480093f4SDimitry Andric   return a.FileEquals(b);
300480093f4SDimitry Andric }
301480093f4SDimitry Andric 
Match(const FileSpec & pattern,const FileSpec & file)302480093f4SDimitry Andric bool FileSpec::Match(const FileSpec &pattern, const FileSpec &file) {
303480093f4SDimitry Andric   if (pattern.GetDirectory())
304480093f4SDimitry Andric     return pattern == file;
305480093f4SDimitry Andric   if (pattern.GetFilename())
306480093f4SDimitry Andric     return pattern.FileEquals(file);
307480093f4SDimitry Andric   return true;
3080b57cec5SDimitry Andric }
3090b57cec5SDimitry Andric 
GuessPathStyle(llvm::StringRef absolute_path)3100b57cec5SDimitry Andric llvm::Optional<FileSpec::Style> FileSpec::GuessPathStyle(llvm::StringRef absolute_path) {
3110b57cec5SDimitry Andric   if (absolute_path.startswith("/"))
3120b57cec5SDimitry Andric     return Style::posix;
3130b57cec5SDimitry Andric   if (absolute_path.startswith(R"(\\)"))
3140b57cec5SDimitry Andric     return Style::windows;
3150b57cec5SDimitry Andric   if (absolute_path.size() > 3 && llvm::isAlpha(absolute_path[0]) &&
3160b57cec5SDimitry Andric       absolute_path.substr(1, 2) == R"(:\)")
3170b57cec5SDimitry Andric     return Style::windows;
3180b57cec5SDimitry Andric   return llvm::None;
3190b57cec5SDimitry Andric }
3200b57cec5SDimitry Andric 
3210b57cec5SDimitry Andric // Dump the object to the supplied stream. If the object contains a valid
3220b57cec5SDimitry Andric // directory name, it will be displayed followed by a directory delimiter, and
3230b57cec5SDimitry Andric // the filename.
Dump(llvm::raw_ostream & s) const324480093f4SDimitry Andric void FileSpec::Dump(llvm::raw_ostream &s) const {
3250b57cec5SDimitry Andric   std::string path{GetPath(true)};
326480093f4SDimitry Andric   s << path;
3270b57cec5SDimitry Andric   char path_separator = GetPreferredPathSeparator(m_style);
3280b57cec5SDimitry Andric   if (!m_filename && !path.empty() && path.back() != path_separator)
329480093f4SDimitry Andric     s << path_separator;
3300b57cec5SDimitry Andric }
3310b57cec5SDimitry Andric 
GetPathStyle() const3320b57cec5SDimitry Andric FileSpec::Style FileSpec::GetPathStyle() const { return m_style; }
3330b57cec5SDimitry Andric 
3340b57cec5SDimitry Andric // Directory string get accessor.
GetDirectory()3350b57cec5SDimitry Andric ConstString &FileSpec::GetDirectory() { return m_directory; }
3360b57cec5SDimitry Andric 
3370b57cec5SDimitry Andric // Directory string const get accessor.
GetDirectory() const3380b57cec5SDimitry Andric ConstString FileSpec::GetDirectory() const { return m_directory; }
3390b57cec5SDimitry Andric 
3400b57cec5SDimitry Andric // Filename string get accessor.
GetFilename()3410b57cec5SDimitry Andric ConstString &FileSpec::GetFilename() { return m_filename; }
3420b57cec5SDimitry Andric 
3430b57cec5SDimitry Andric // Filename string const get accessor.
GetFilename() const3440b57cec5SDimitry Andric ConstString FileSpec::GetFilename() const { return m_filename; }
3450b57cec5SDimitry Andric 
3460b57cec5SDimitry Andric // Extract the directory and path into a fixed buffer. This is needed as the
3470b57cec5SDimitry Andric // directory and path are stored in separate string values.
GetPath(char * path,size_t path_max_len,bool denormalize) const3480b57cec5SDimitry Andric size_t FileSpec::GetPath(char *path, size_t path_max_len,
3490b57cec5SDimitry Andric                          bool denormalize) const {
3500b57cec5SDimitry Andric   if (!path)
3510b57cec5SDimitry Andric     return 0;
3520b57cec5SDimitry Andric 
3530b57cec5SDimitry Andric   std::string result = GetPath(denormalize);
3540b57cec5SDimitry Andric   ::snprintf(path, path_max_len, "%s", result.c_str());
3550b57cec5SDimitry Andric   return std::min(path_max_len - 1, result.length());
3560b57cec5SDimitry Andric }
3570b57cec5SDimitry Andric 
GetPath(bool denormalize) const3580b57cec5SDimitry Andric std::string FileSpec::GetPath(bool denormalize) const {
3590b57cec5SDimitry Andric   llvm::SmallString<64> result;
3600b57cec5SDimitry Andric   GetPath(result, denormalize);
3610b57cec5SDimitry Andric   return std::string(result.begin(), result.end());
3620b57cec5SDimitry Andric }
3630b57cec5SDimitry Andric 
GetCString(bool denormalize) const3640b57cec5SDimitry Andric const char *FileSpec::GetCString(bool denormalize) const {
3650b57cec5SDimitry Andric   return ConstString{GetPath(denormalize)}.AsCString(nullptr);
3660b57cec5SDimitry Andric }
3670b57cec5SDimitry Andric 
GetPath(llvm::SmallVectorImpl<char> & path,bool denormalize) const3680b57cec5SDimitry Andric void FileSpec::GetPath(llvm::SmallVectorImpl<char> &path,
3690b57cec5SDimitry Andric                        bool denormalize) const {
3700b57cec5SDimitry Andric   path.append(m_directory.GetStringRef().begin(),
3710b57cec5SDimitry Andric               m_directory.GetStringRef().end());
3720b57cec5SDimitry Andric   // Since the path was normalized and all paths use '/' when stored in these
3730b57cec5SDimitry Andric   // objects, we don't need to look for the actual syntax specific path
3740b57cec5SDimitry Andric   // separator, we just look for and insert '/'.
3750b57cec5SDimitry Andric   if (m_directory && m_filename && m_directory.GetStringRef().back() != '/' &&
3760b57cec5SDimitry Andric       m_filename.GetStringRef().back() != '/')
3770b57cec5SDimitry Andric     path.insert(path.end(), '/');
3780b57cec5SDimitry Andric   path.append(m_filename.GetStringRef().begin(),
3790b57cec5SDimitry Andric               m_filename.GetStringRef().end());
3800b57cec5SDimitry Andric   if (denormalize && !path.empty())
3810b57cec5SDimitry Andric     Denormalize(path, m_style);
3820b57cec5SDimitry Andric }
3830b57cec5SDimitry Andric 
GetFileNameExtension() const3840b57cec5SDimitry Andric ConstString FileSpec::GetFileNameExtension() const {
3850b57cec5SDimitry Andric   return ConstString(
3860b57cec5SDimitry Andric       llvm::sys::path::extension(m_filename.GetStringRef(), m_style));
3870b57cec5SDimitry Andric }
3880b57cec5SDimitry Andric 
GetFileNameStrippingExtension() const3890b57cec5SDimitry Andric ConstString FileSpec::GetFileNameStrippingExtension() const {
3900b57cec5SDimitry Andric   return ConstString(llvm::sys::path::stem(m_filename.GetStringRef(), m_style));
3910b57cec5SDimitry Andric }
3920b57cec5SDimitry Andric 
3930b57cec5SDimitry Andric // Return the size in bytes that this object takes in memory. This returns the
3940b57cec5SDimitry Andric // size in bytes of this object, not any shared string values it may refer to.
MemorySize() const3950b57cec5SDimitry Andric size_t FileSpec::MemorySize() const {
3960b57cec5SDimitry Andric   return m_filename.MemorySize() + m_directory.MemorySize();
3970b57cec5SDimitry Andric }
3980b57cec5SDimitry Andric 
3990b57cec5SDimitry Andric FileSpec
CopyByAppendingPathComponent(llvm::StringRef component) const4000b57cec5SDimitry Andric FileSpec::CopyByAppendingPathComponent(llvm::StringRef component) const {
4010b57cec5SDimitry Andric   FileSpec ret = *this;
4020b57cec5SDimitry Andric   ret.AppendPathComponent(component);
4030b57cec5SDimitry Andric   return ret;
4040b57cec5SDimitry Andric }
4050b57cec5SDimitry Andric 
CopyByRemovingLastPathComponent() const4060b57cec5SDimitry Andric FileSpec FileSpec::CopyByRemovingLastPathComponent() const {
4070b57cec5SDimitry Andric   llvm::SmallString<64> current_path;
4080b57cec5SDimitry Andric   GetPath(current_path, false);
4090b57cec5SDimitry Andric   if (llvm::sys::path::has_parent_path(current_path, m_style))
4100b57cec5SDimitry Andric     return FileSpec(llvm::sys::path::parent_path(current_path, m_style),
4110b57cec5SDimitry Andric                     m_style);
4120b57cec5SDimitry Andric   return *this;
4130b57cec5SDimitry Andric }
4140b57cec5SDimitry Andric 
GetLastPathComponent() const4150b57cec5SDimitry Andric ConstString FileSpec::GetLastPathComponent() const {
4160b57cec5SDimitry Andric   llvm::SmallString<64> current_path;
4170b57cec5SDimitry Andric   GetPath(current_path, false);
4180b57cec5SDimitry Andric   return ConstString(llvm::sys::path::filename(current_path, m_style));
4190b57cec5SDimitry Andric }
4200b57cec5SDimitry Andric 
PrependPathComponent(llvm::StringRef component)4210b57cec5SDimitry Andric void FileSpec::PrependPathComponent(llvm::StringRef component) {
4220b57cec5SDimitry Andric   llvm::SmallString<64> new_path(component);
4230b57cec5SDimitry Andric   llvm::SmallString<64> current_path;
4240b57cec5SDimitry Andric   GetPath(current_path, false);
4250b57cec5SDimitry Andric   llvm::sys::path::append(new_path,
4260b57cec5SDimitry Andric                           llvm::sys::path::begin(current_path, m_style),
4270b57cec5SDimitry Andric                           llvm::sys::path::end(current_path), m_style);
4280b57cec5SDimitry Andric   SetFile(new_path, m_style);
4290b57cec5SDimitry Andric }
4300b57cec5SDimitry Andric 
PrependPathComponent(const FileSpec & new_path)4310b57cec5SDimitry Andric void FileSpec::PrependPathComponent(const FileSpec &new_path) {
4320b57cec5SDimitry Andric   return PrependPathComponent(new_path.GetPath(false));
4330b57cec5SDimitry Andric }
4340b57cec5SDimitry Andric 
AppendPathComponent(llvm::StringRef component)4350b57cec5SDimitry Andric void FileSpec::AppendPathComponent(llvm::StringRef component) {
4360b57cec5SDimitry Andric   llvm::SmallString<64> current_path;
4370b57cec5SDimitry Andric   GetPath(current_path, false);
4380b57cec5SDimitry Andric   llvm::sys::path::append(current_path, m_style, component);
4390b57cec5SDimitry Andric   SetFile(current_path, m_style);
4400b57cec5SDimitry Andric }
4410b57cec5SDimitry Andric 
AppendPathComponent(const FileSpec & new_path)4420b57cec5SDimitry Andric void FileSpec::AppendPathComponent(const FileSpec &new_path) {
4430b57cec5SDimitry Andric   return AppendPathComponent(new_path.GetPath(false));
4440b57cec5SDimitry Andric }
4450b57cec5SDimitry Andric 
RemoveLastPathComponent()4460b57cec5SDimitry Andric bool FileSpec::RemoveLastPathComponent() {
4470b57cec5SDimitry Andric   llvm::SmallString<64> current_path;
4480b57cec5SDimitry Andric   GetPath(current_path, false);
4490b57cec5SDimitry Andric   if (llvm::sys::path::has_parent_path(current_path, m_style)) {
4500b57cec5SDimitry Andric     SetFile(llvm::sys::path::parent_path(current_path, m_style));
4510b57cec5SDimitry Andric     return true;
4520b57cec5SDimitry Andric   }
4530b57cec5SDimitry Andric   return false;
4540b57cec5SDimitry Andric }
4550b57cec5SDimitry Andric /// Returns true if the filespec represents an implementation source
4560b57cec5SDimitry Andric /// file (files with a ".c", ".cpp", ".m", ".mm" (many more)
4570b57cec5SDimitry Andric /// extension).
4580b57cec5SDimitry Andric ///
4590b57cec5SDimitry Andric /// \return
4600b57cec5SDimitry Andric ///     \b true if the filespec represents an implementation source
4610b57cec5SDimitry Andric ///     file, \b false otherwise.
IsSourceImplementationFile() const4620b57cec5SDimitry Andric bool FileSpec::IsSourceImplementationFile() const {
4630b57cec5SDimitry Andric   ConstString extension(GetFileNameExtension());
4640b57cec5SDimitry Andric   if (!extension)
4650b57cec5SDimitry Andric     return false;
4660b57cec5SDimitry Andric 
4670b57cec5SDimitry Andric   static RegularExpression g_source_file_regex(llvm::StringRef(
4680b57cec5SDimitry Andric       "^.([cC]|[mM]|[mM][mM]|[cC][pP][pP]|[cC]\\+\\+|[cC][xX][xX]|[cC][cC]|["
4690b57cec5SDimitry Andric       "cC][pP]|[sS]|[aA][sS][mM]|[fF]|[fF]77|[fF]90|[fF]95|[fF]03|[fF][oO]["
4700b57cec5SDimitry Andric       "rR]|[fF][tT][nN]|[fF][pP][pP]|[aA][dD][aA]|[aA][dD][bB]|[aA][dD][sS])"
4710b57cec5SDimitry Andric       "$"));
4720b57cec5SDimitry Andric   return g_source_file_regex.Execute(extension.GetStringRef());
4730b57cec5SDimitry Andric }
4740b57cec5SDimitry Andric 
IsRelative() const4750b57cec5SDimitry Andric bool FileSpec::IsRelative() const {
4760b57cec5SDimitry Andric   return !IsAbsolute();
4770b57cec5SDimitry Andric }
4780b57cec5SDimitry Andric 
IsAbsolute() const4790b57cec5SDimitry Andric bool FileSpec::IsAbsolute() const {
4800b57cec5SDimitry Andric   llvm::SmallString<64> current_path;
4810b57cec5SDimitry Andric   GetPath(current_path, false);
4820b57cec5SDimitry Andric 
4830b57cec5SDimitry Andric   // Early return if the path is empty.
4840b57cec5SDimitry Andric   if (current_path.empty())
4850b57cec5SDimitry Andric     return false;
4860b57cec5SDimitry Andric 
4870b57cec5SDimitry Andric   // We consider paths starting with ~ to be absolute.
4880b57cec5SDimitry Andric   if (current_path[0] == '~')
4890b57cec5SDimitry Andric     return true;
4900b57cec5SDimitry Andric 
4910b57cec5SDimitry Andric   return llvm::sys::path::is_absolute(current_path, m_style);
4920b57cec5SDimitry Andric }
4930b57cec5SDimitry Andric 
MakeAbsolute(const FileSpec & dir)4940b57cec5SDimitry Andric void FileSpec::MakeAbsolute(const FileSpec &dir) {
4950b57cec5SDimitry Andric   if (IsRelative())
4960b57cec5SDimitry Andric     PrependPathComponent(dir);
4970b57cec5SDimitry Andric }
4980b57cec5SDimitry Andric 
format(const FileSpec & F,raw_ostream & Stream,StringRef Style)4990b57cec5SDimitry Andric void llvm::format_provider<FileSpec>::format(const FileSpec &F,
5000b57cec5SDimitry Andric                                              raw_ostream &Stream,
5010b57cec5SDimitry Andric                                              StringRef Style) {
502*5f7ddb14SDimitry Andric   assert((Style.empty() || Style.equals_insensitive("F") ||
503*5f7ddb14SDimitry Andric           Style.equals_insensitive("D")) &&
5040b57cec5SDimitry Andric          "Invalid FileSpec style!");
5050b57cec5SDimitry Andric 
5060b57cec5SDimitry Andric   StringRef dir = F.GetDirectory().GetStringRef();
5070b57cec5SDimitry Andric   StringRef file = F.GetFilename().GetStringRef();
5080b57cec5SDimitry Andric 
5090b57cec5SDimitry Andric   if (dir.empty() && file.empty()) {
5100b57cec5SDimitry Andric     Stream << "(empty)";
5110b57cec5SDimitry Andric     return;
5120b57cec5SDimitry Andric   }
5130b57cec5SDimitry Andric 
514*5f7ddb14SDimitry Andric   if (Style.equals_insensitive("F")) {
5150b57cec5SDimitry Andric     Stream << (file.empty() ? "(empty)" : file);
5160b57cec5SDimitry Andric     return;
5170b57cec5SDimitry Andric   }
5180b57cec5SDimitry Andric 
5190b57cec5SDimitry Andric   // Style is either D or empty, either way we need to print the directory.
5200b57cec5SDimitry Andric   if (!dir.empty()) {
5210b57cec5SDimitry Andric     // Directory is stored in normalized form, which might be different than
5220b57cec5SDimitry Andric     // preferred form.  In order to handle this, we need to cut off the
5230b57cec5SDimitry Andric     // filename, then denormalize, then write the entire denorm'ed directory.
5240b57cec5SDimitry Andric     llvm::SmallString<64> denormalized_dir = dir;
5250b57cec5SDimitry Andric     Denormalize(denormalized_dir, F.GetPathStyle());
5260b57cec5SDimitry Andric     Stream << denormalized_dir;
5270b57cec5SDimitry Andric     Stream << GetPreferredPathSeparator(F.GetPathStyle());
5280b57cec5SDimitry Andric   }
5290b57cec5SDimitry Andric 
530*5f7ddb14SDimitry Andric   if (Style.equals_insensitive("D")) {
5310b57cec5SDimitry Andric     // We only want to print the directory, so now just exit.
5320b57cec5SDimitry Andric     if (dir.empty())
5330b57cec5SDimitry Andric       Stream << "(empty)";
5340b57cec5SDimitry Andric     return;
5350b57cec5SDimitry Andric   }
5360b57cec5SDimitry Andric 
5370b57cec5SDimitry Andric   if (!file.empty())
5380b57cec5SDimitry Andric     Stream << file;
5390b57cec5SDimitry Andric }
5405ffd83dbSDimitry Andric 
enumeration(IO & io,FileSpecStyle & value)5415ffd83dbSDimitry Andric void llvm::yaml::ScalarEnumerationTraits<FileSpecStyle>::enumeration(
5425ffd83dbSDimitry Andric     IO &io, FileSpecStyle &value) {
5435ffd83dbSDimitry Andric   io.enumCase(value, "windows", FileSpecStyle(FileSpec::Style::windows));
5445ffd83dbSDimitry Andric   io.enumCase(value, "posix", FileSpecStyle(FileSpec::Style::posix));
5455ffd83dbSDimitry Andric   io.enumCase(value, "native", FileSpecStyle(FileSpec::Style::native));
5465ffd83dbSDimitry Andric }
5475ffd83dbSDimitry Andric 
mapping(IO & io,FileSpec & f)5485ffd83dbSDimitry Andric void llvm::yaml::MappingTraits<FileSpec>::mapping(IO &io, FileSpec &f) {
5495ffd83dbSDimitry Andric   io.mapRequired("directory", f.m_directory);
5505ffd83dbSDimitry Andric   io.mapRequired("file", f.m_filename);
5515ffd83dbSDimitry Andric   io.mapRequired("resolved", f.m_is_resolved);
5525ffd83dbSDimitry Andric   FileSpecStyle style = f.m_style;
5535ffd83dbSDimitry Andric   io.mapRequired("style", style);
5545ffd83dbSDimitry Andric   f.m_style = style;
5555ffd83dbSDimitry Andric }
556