1 //===-- FileSpec.cpp --------------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "lldb/Utility/FileSpec.h" 11 #include "lldb/Utility/RegularExpression.h" 12 #include "lldb/Utility/Stream.h" 13 #include "lldb/Utility/TildeExpressionResolver.h" 14 15 #include "llvm/ADT/SmallString.h" // for SmallString 16 #include "llvm/ADT/SmallVector.h" // for SmallVectorTemplat... 17 #include "llvm/ADT/StringRef.h" 18 #include "llvm/ADT/Triple.h" // for Triple 19 #include "llvm/ADT/Twine.h" // for Twine 20 #include "llvm/Support/ErrorOr.h" // for ErrorOr 21 #include "llvm/Support/FileSystem.h" 22 #include "llvm/Support/Program.h" 23 #include "llvm/Support/raw_ostream.h" // for raw_ostream, fs 24 25 #include <algorithm> // for replace, min, unique 26 #include <system_error> // for error_code 27 #include <vector> // for vector 28 29 #include <assert.h> // for assert 30 #include <stdio.h> // for size_t, NULL, snpr... 31 #include <string.h> // for strcmp 32 33 using namespace lldb; 34 using namespace lldb_private; 35 36 namespace { 37 38 static constexpr FileSpec::Style GetNativeStyle() { 39 #if defined(_WIN32) 40 return FileSpec::Style::windows; 41 #else 42 return FileSpec::Style::posix; 43 #endif 44 } 45 46 bool PathStyleIsPosix(FileSpec::Style style) { 47 return (style == FileSpec::Style::posix || 48 (style == FileSpec::Style::native && 49 GetNativeStyle() == FileSpec::Style::posix)); 50 } 51 52 const char *GetPathSeparators(FileSpec::Style style) { 53 return PathStyleIsPosix(style) ? "/" : "\\/"; 54 } 55 56 char GetPreferredPathSeparator(FileSpec::Style style) { 57 return GetPathSeparators(style)[0]; 58 } 59 60 bool IsPathSeparator(char value, FileSpec::Style style) { 61 return value == '/' || (!PathStyleIsPosix(style) && value == '\\'); 62 } 63 64 void Denormalize(llvm::SmallVectorImpl<char> &path, FileSpec::Style style) { 65 if (PathStyleIsPosix(style)) 66 return; 67 68 std::replace(path.begin(), path.end(), '/', '\\'); 69 } 70 71 bool PathIsRelative(llvm::StringRef path, FileSpec::Style style) { 72 73 if (path.empty()) 74 return false; 75 76 if (PathStyleIsPosix(style)) { 77 // If the path doesn't start with '/' or '~', return true 78 switch (path[0]) { 79 case '/': 80 case '~': 81 return false; 82 default: 83 return true; 84 } 85 } else { 86 if (path.size() >= 2 && path[1] == ':') 87 return false; 88 if (path[0] == '/') 89 return false; 90 return true; 91 } 92 return false; 93 } 94 95 } // end anonymous namespace 96 97 void FileSpec::Resolve(llvm::SmallVectorImpl<char> &path) { 98 if (path.empty()) 99 return; 100 101 llvm::SmallString<32> Source(path.begin(), path.end()); 102 StandardTildeExpressionResolver Resolver; 103 Resolver.ResolveFullPath(Source, path); 104 105 // Save a copy of the original path that's passed in 106 llvm::SmallString<128> original_path(path.begin(), path.end()); 107 108 llvm::sys::fs::make_absolute(path); 109 if (!llvm::sys::fs::exists(path)) { 110 path.clear(); 111 path.append(original_path.begin(), original_path.end()); 112 } 113 } 114 115 FileSpec::FileSpec() : m_style(GetNativeStyle()) {} 116 117 //------------------------------------------------------------------ 118 // Default constructor that can take an optional full path to a file on disk. 119 //------------------------------------------------------------------ 120 FileSpec::FileSpec(llvm::StringRef path, bool resolve_path, Style style) 121 : m_style(style) { 122 SetFile(path, resolve_path, style); 123 } 124 125 FileSpec::FileSpec(llvm::StringRef path, bool resolve_path, 126 const llvm::Triple &Triple) 127 : FileSpec{path, resolve_path, 128 Triple.isOSWindows() ? Style::windows : Style::posix} {} 129 130 //------------------------------------------------------------------ 131 // Copy constructor 132 //------------------------------------------------------------------ 133 FileSpec::FileSpec(const FileSpec &rhs) 134 : m_directory(rhs.m_directory), m_filename(rhs.m_filename), 135 m_is_resolved(rhs.m_is_resolved), m_style(rhs.m_style) {} 136 137 //------------------------------------------------------------------ 138 // Copy constructor 139 //------------------------------------------------------------------ 140 FileSpec::FileSpec(const FileSpec *rhs) : m_directory(), m_filename() { 141 if (rhs) 142 *this = *rhs; 143 } 144 145 //------------------------------------------------------------------ 146 // Virtual destructor in case anyone inherits from this class. 147 //------------------------------------------------------------------ 148 FileSpec::~FileSpec() {} 149 150 namespace { 151 //------------------------------------------------------------------ 152 /// Safely get a character at the specified index. 153 /// 154 /// @param[in] path 155 /// A full, partial, or relative path to a file. 156 /// 157 /// @param[in] i 158 /// An index into path which may or may not be valid. 159 /// 160 /// @return 161 /// The character at index \a i if the index is valid, or 0 if 162 /// the index is not valid. 163 //------------------------------------------------------------------ 164 inline char safeCharAtIndex(const llvm::StringRef &path, size_t i) { 165 if (i < path.size()) 166 return path[i]; 167 return 0; 168 } 169 170 //------------------------------------------------------------------ 171 /// Check if a path needs to be normalized. 172 /// 173 /// Check if a path needs to be normalized. We currently consider a 174 /// path to need normalization if any of the following are true 175 /// - path contains "/./" 176 /// - path contains "/../" 177 /// - path contains "//" 178 /// - path ends with "/" 179 /// Paths that start with "./" or with "../" are not considered to 180 /// need normalization since we aren't trying to resolve the path, 181 /// we are just trying to remove redundant things from the path. 182 /// 183 /// @param[in] path 184 /// A full, partial, or relative path to a file. 185 /// 186 /// @return 187 /// Returns \b true if the path needs to be normalized. 188 //------------------------------------------------------------------ 189 bool needsNormalization(const llvm::StringRef &path) { 190 if (path.empty()) 191 return false; 192 // We strip off leading "." values so these paths need to be normalized 193 if (path[0] == '.') 194 return true; 195 for (auto i = path.find_first_of("\\/"); i != llvm::StringRef::npos; 196 i = path.find_first_of("\\/", i + 1)) { 197 const auto next = safeCharAtIndex(path, i+1); 198 switch (next) { 199 case 0: 200 // path separator char at the end of the string which should be 201 // stripped unless it is the one and only character 202 return i > 0; 203 case '/': 204 case '\\': 205 // two path separator chars in the middle of a path needs to be 206 // normalized 207 if (i > 0) 208 return true; 209 ++i; 210 break; 211 212 case '.': { 213 const auto next_next = safeCharAtIndex(path, i+2); 214 switch (next_next) { 215 default: break; 216 case 0: return true; // ends with "/." 217 case '/': 218 case '\\': 219 return true; // contains "/./" 220 case '.': { 221 const auto next_next_next = safeCharAtIndex(path, i+3); 222 switch (next_next_next) { 223 default: break; 224 case 0: return true; // ends with "/.." 225 case '/': 226 case '\\': 227 return true; // contains "/../" 228 } 229 break; 230 } 231 } 232 } 233 break; 234 235 default: 236 break; 237 } 238 } 239 return false; 240 } 241 242 243 } 244 //------------------------------------------------------------------ 245 // Assignment operator. 246 //------------------------------------------------------------------ 247 const FileSpec &FileSpec::operator=(const FileSpec &rhs) { 248 if (this != &rhs) { 249 m_directory = rhs.m_directory; 250 m_filename = rhs.m_filename; 251 m_is_resolved = rhs.m_is_resolved; 252 m_style = rhs.m_style; 253 } 254 return *this; 255 } 256 257 //------------------------------------------------------------------ 258 // Update the contents of this object with a new path. The path will be split 259 // up into a directory and filename and stored as uniqued string values for 260 // quick comparison and efficient memory usage. 261 //------------------------------------------------------------------ 262 void FileSpec::SetFile(llvm::StringRef pathname, bool resolve, Style style) { 263 m_filename.Clear(); 264 m_directory.Clear(); 265 m_is_resolved = false; 266 m_style = (style == Style::native) ? GetNativeStyle() : style; 267 268 if (pathname.empty()) 269 return; 270 271 llvm::SmallString<64> resolved(pathname); 272 273 if (resolve) { 274 FileSpec::Resolve(resolved); 275 m_is_resolved = true; 276 } 277 278 // Normalize the path by removing ".", ".." and other redundant components. 279 if (needsNormalization(resolved)) 280 llvm::sys::path::remove_dots(resolved, true, m_style); 281 282 // Normalize back slashes to forward slashes 283 if (m_style == Style::windows) 284 std::replace(resolved.begin(), resolved.end(), '\\', '/'); 285 286 if (resolved.empty()) { 287 // If we have no path after normalization set the path to the current 288 // directory. This matches what python does and also a few other path 289 // utilities. 290 m_filename.SetString("."); 291 return; 292 } 293 294 m_filename.SetString(llvm::sys::path::filename(resolved, m_style)); 295 llvm::StringRef dir = llvm::sys::path::parent_path(resolved, m_style); 296 if (!dir.empty()) 297 m_directory.SetString(dir); 298 } 299 300 void FileSpec::SetFile(llvm::StringRef path, bool resolve, 301 const llvm::Triple &Triple) { 302 return SetFile(path, resolve, 303 Triple.isOSWindows() ? Style::windows : Style::posix); 304 } 305 306 //---------------------------------------------------------------------- 307 // Convert to pointer operator. This allows code to check any FileSpec objects 308 // to see if they contain anything valid using code such as: 309 // 310 // if (file_spec) 311 // {} 312 //---------------------------------------------------------------------- 313 FileSpec::operator bool() const { return m_filename || m_directory; } 314 315 //---------------------------------------------------------------------- 316 // Logical NOT operator. This allows code to check any FileSpec objects to see 317 // if they are invalid using code such as: 318 // 319 // if (!file_spec) 320 // {} 321 //---------------------------------------------------------------------- 322 bool FileSpec::operator!() const { return !m_directory && !m_filename; } 323 324 bool FileSpec::DirectoryEquals(const FileSpec &rhs) const { 325 const bool case_sensitive = IsCaseSensitive() || rhs.IsCaseSensitive(); 326 return ConstString::Equals(m_directory, rhs.m_directory, case_sensitive); 327 } 328 329 bool FileSpec::FileEquals(const FileSpec &rhs) const { 330 const bool case_sensitive = IsCaseSensitive() || rhs.IsCaseSensitive(); 331 return ConstString::Equals(m_filename, rhs.m_filename, case_sensitive); 332 } 333 334 //------------------------------------------------------------------ 335 // Equal to operator 336 //------------------------------------------------------------------ 337 bool FileSpec::operator==(const FileSpec &rhs) const { 338 if (!FileEquals(rhs)) 339 return false; 340 if (DirectoryEquals(rhs)) 341 return true; 342 343 // TODO: determine if we want to keep this code in here. 344 // The code below was added to handle a case where we were trying to set a 345 // file and line breakpoint and one path was resolved, and the other not and 346 // the directory was in a mount point that resolved to a more complete path: 347 // "/tmp/a.c" == "/private/tmp/a.c". I might end up pulling this out... 348 if (IsResolved() && rhs.IsResolved()) { 349 // Both paths are resolved, no need to look further... 350 return false; 351 } 352 353 FileSpec resolved_lhs(*this); 354 355 // If "this" isn't resolved, resolve it 356 if (!IsResolved()) { 357 if (resolved_lhs.ResolvePath()) { 358 // This path wasn't resolved but now it is. Check if the resolved 359 // directory is the same as our unresolved directory, and if so, we can 360 // mark this object as resolved to avoid more future resolves 361 m_is_resolved = (m_directory == resolved_lhs.m_directory); 362 } else 363 return false; 364 } 365 366 FileSpec resolved_rhs(rhs); 367 if (!rhs.IsResolved()) { 368 if (resolved_rhs.ResolvePath()) { 369 // rhs's path wasn't resolved but now it is. Check if the resolved 370 // directory is the same as rhs's unresolved directory, and if so, we can 371 // mark this object as resolved to avoid more future resolves 372 rhs.m_is_resolved = (rhs.m_directory == resolved_rhs.m_directory); 373 } else 374 return false; 375 } 376 377 // If we reach this point in the code we were able to resolve both paths and 378 // since we only resolve the paths if the basenames are equal, then we can 379 // just check if both directories are equal... 380 return DirectoryEquals(rhs); 381 } 382 383 //------------------------------------------------------------------ 384 // Not equal to operator 385 //------------------------------------------------------------------ 386 bool FileSpec::operator!=(const FileSpec &rhs) const { return !(*this == rhs); } 387 388 //------------------------------------------------------------------ 389 // Less than operator 390 //------------------------------------------------------------------ 391 bool FileSpec::operator<(const FileSpec &rhs) const { 392 return FileSpec::Compare(*this, rhs, true) < 0; 393 } 394 395 //------------------------------------------------------------------ 396 // Dump a FileSpec object to a stream 397 //------------------------------------------------------------------ 398 Stream &lldb_private::operator<<(Stream &s, const FileSpec &f) { 399 f.Dump(&s); 400 return s; 401 } 402 403 //------------------------------------------------------------------ 404 // Clear this object by releasing both the directory and filename string values 405 // and making them both the empty string. 406 //------------------------------------------------------------------ 407 void FileSpec::Clear() { 408 m_directory.Clear(); 409 m_filename.Clear(); 410 } 411 412 //------------------------------------------------------------------ 413 // Compare two FileSpec objects. If "full" is true, then both the directory and 414 // the filename must match. If "full" is false, then the directory names for 415 // "a" and "b" are only compared if they are both non-empty. This allows a 416 // FileSpec object to only contain a filename and it can match FileSpec objects 417 // that have matching filenames with different paths. 418 // 419 // Return -1 if the "a" is less than "b", 0 if "a" is equal to "b" and "1" if 420 // "a" is greater than "b". 421 //------------------------------------------------------------------ 422 int FileSpec::Compare(const FileSpec &a, const FileSpec &b, bool full) { 423 int result = 0; 424 425 // case sensitivity of compare 426 const bool case_sensitive = a.IsCaseSensitive() || b.IsCaseSensitive(); 427 428 // If full is true, then we must compare both the directory and filename. 429 430 // If full is false, then if either directory is empty, then we match on the 431 // basename only, and if both directories have valid values, we still do a 432 // full compare. This allows for matching when we just have a filename in one 433 // of the FileSpec objects. 434 435 if (full || (a.m_directory && b.m_directory)) { 436 result = ConstString::Compare(a.m_directory, b.m_directory, case_sensitive); 437 if (result) 438 return result; 439 } 440 return ConstString::Compare(a.m_filename, b.m_filename, case_sensitive); 441 } 442 443 bool FileSpec::Equal(const FileSpec &a, const FileSpec &b, bool full) { 444 445 // case sensitivity of equality test 446 const bool case_sensitive = a.IsCaseSensitive() || b.IsCaseSensitive(); 447 448 const bool filenames_equal = ConstString::Equals(a.m_filename, 449 b.m_filename, 450 case_sensitive); 451 452 if (!filenames_equal) 453 return false; 454 455 if (!full && (a.GetDirectory().IsEmpty() || b.GetDirectory().IsEmpty())) 456 return filenames_equal; 457 458 return a == b; 459 } 460 461 //------------------------------------------------------------------ 462 // Dump the object to the supplied stream. If the object contains a valid 463 // directory name, it will be displayed followed by a directory delimiter, and 464 // the filename. 465 //------------------------------------------------------------------ 466 void FileSpec::Dump(Stream *s) const { 467 if (s) { 468 std::string path{GetPath(true)}; 469 s->PutCString(path); 470 char path_separator = GetPreferredPathSeparator(m_style); 471 if (!m_filename && !path.empty() && path.back() != path_separator) 472 s->PutChar(path_separator); 473 } 474 } 475 476 //------------------------------------------------------------------ 477 // Returns true if the file exists. 478 //------------------------------------------------------------------ 479 bool FileSpec::Exists() const { return llvm::sys::fs::exists(GetPath()); } 480 481 bool FileSpec::Readable() const { 482 return GetPermissions() & llvm::sys::fs::perms::all_read; 483 } 484 485 bool FileSpec::ResolveExecutableLocation() { 486 // CLEANUP: Use StringRef for string handling. 487 if (!m_directory) { 488 const char *file_cstr = m_filename.GetCString(); 489 if (file_cstr) { 490 const std::string file_str(file_cstr); 491 llvm::ErrorOr<std::string> error_or_path = 492 llvm::sys::findProgramByName(file_str); 493 if (!error_or_path) 494 return false; 495 std::string path = error_or_path.get(); 496 llvm::StringRef dir_ref = llvm::sys::path::parent_path(path); 497 if (!dir_ref.empty()) { 498 // FindProgramByName returns "." if it can't find the file. 499 if (strcmp(".", dir_ref.data()) == 0) 500 return false; 501 502 m_directory.SetCString(dir_ref.data()); 503 if (Exists()) 504 return true; 505 else { 506 // If FindProgramByName found the file, it returns the directory + 507 // filename in its return results. We need to separate them. 508 FileSpec tmp_file(dir_ref.data(), false); 509 if (tmp_file.Exists()) { 510 m_directory = tmp_file.m_directory; 511 return true; 512 } 513 } 514 } 515 } 516 } 517 518 return false; 519 } 520 521 bool FileSpec::ResolvePath() { 522 if (m_is_resolved) 523 return true; // We have already resolved this path 524 525 // SetFile(...) will set m_is_resolved correctly if it can resolve the path 526 SetFile(GetPath(false), true); 527 return m_is_resolved; 528 } 529 530 uint64_t FileSpec::GetByteSize() const { 531 uint64_t Size = 0; 532 if (llvm::sys::fs::file_size(GetPath(), Size)) 533 return 0; 534 return Size; 535 } 536 537 FileSpec::Style FileSpec::GetPathStyle() const { return m_style; } 538 539 uint32_t FileSpec::GetPermissions() const { 540 namespace fs = llvm::sys::fs; 541 fs::file_status st; 542 if (fs::status(GetPath(), st, false)) 543 return fs::perms::perms_not_known; 544 545 return st.permissions(); 546 } 547 548 //------------------------------------------------------------------ 549 // Directory string get accessor. 550 //------------------------------------------------------------------ 551 ConstString &FileSpec::GetDirectory() { return m_directory; } 552 553 //------------------------------------------------------------------ 554 // Directory string const get accessor. 555 //------------------------------------------------------------------ 556 const ConstString &FileSpec::GetDirectory() const { return m_directory; } 557 558 //------------------------------------------------------------------ 559 // Filename string get accessor. 560 //------------------------------------------------------------------ 561 ConstString &FileSpec::GetFilename() { return m_filename; } 562 563 //------------------------------------------------------------------ 564 // Filename string const get accessor. 565 //------------------------------------------------------------------ 566 const ConstString &FileSpec::GetFilename() const { return m_filename; } 567 568 //------------------------------------------------------------------ 569 // Extract the directory and path into a fixed buffer. This is needed as the 570 // directory and path are stored in separate string values. 571 //------------------------------------------------------------------ 572 size_t FileSpec::GetPath(char *path, size_t path_max_len, 573 bool denormalize) const { 574 if (!path) 575 return 0; 576 577 std::string result = GetPath(denormalize); 578 ::snprintf(path, path_max_len, "%s", result.c_str()); 579 return std::min(path_max_len - 1, result.length()); 580 } 581 582 std::string FileSpec::GetPath(bool denormalize) const { 583 llvm::SmallString<64> result; 584 GetPath(result, denormalize); 585 return std::string(result.begin(), result.end()); 586 } 587 588 const char *FileSpec::GetCString(bool denormalize) const { 589 return ConstString{GetPath(denormalize)}.AsCString(NULL); 590 } 591 592 void FileSpec::GetPath(llvm::SmallVectorImpl<char> &path, 593 bool denormalize) const { 594 path.append(m_directory.GetStringRef().begin(), 595 m_directory.GetStringRef().end()); 596 // Since the path was normalized and all paths use '/' when stored in these 597 // objects, we don't need to look for the actual syntax specific path 598 // separator, we just look for and insert '/'. 599 if (m_directory && m_filename && m_directory.GetStringRef().back() != '/' && 600 m_filename.GetStringRef().back() != '/') 601 path.insert(path.end(), '/'); 602 path.append(m_filename.GetStringRef().begin(), 603 m_filename.GetStringRef().end()); 604 if (denormalize && !path.empty()) 605 Denormalize(path, m_style); 606 } 607 608 ConstString FileSpec::GetFileNameExtension() const { 609 if (m_filename) { 610 const char *filename = m_filename.GetCString(); 611 const char *dot_pos = strrchr(filename, '.'); 612 if (dot_pos && dot_pos[1] != '\0') 613 return ConstString(dot_pos + 1); 614 } 615 return ConstString(); 616 } 617 618 ConstString FileSpec::GetFileNameStrippingExtension() const { 619 const char *filename = m_filename.GetCString(); 620 if (filename == NULL) 621 return ConstString(); 622 623 const char *dot_pos = strrchr(filename, '.'); 624 if (dot_pos == NULL) 625 return m_filename; 626 627 return ConstString(filename, dot_pos - filename); 628 } 629 630 //------------------------------------------------------------------ 631 // Return the size in bytes that this object takes in memory. This returns the 632 // size in bytes of this object, not any shared string values it may refer to. 633 //------------------------------------------------------------------ 634 size_t FileSpec::MemorySize() const { 635 return m_filename.MemorySize() + m_directory.MemorySize(); 636 } 637 638 void FileSpec::EnumerateDirectory(llvm::StringRef dir_path, 639 bool find_directories, bool find_files, 640 bool find_other, 641 EnumerateDirectoryCallbackType callback, 642 void *callback_baton) { 643 namespace fs = llvm::sys::fs; 644 std::error_code EC; 645 fs::recursive_directory_iterator Iter(dir_path, EC); 646 fs::recursive_directory_iterator End; 647 for (; Iter != End && !EC; Iter.increment(EC)) { 648 const auto &Item = *Iter; 649 llvm::ErrorOr<fs::basic_file_status> Status = Item.status(); 650 if (!Status) 651 break; 652 if (!find_files && fs::is_regular_file(*Status)) 653 continue; 654 if (!find_directories && fs::is_directory(*Status)) 655 continue; 656 if (!find_other && fs::is_other(*Status)) 657 continue; 658 659 FileSpec Spec(Item.path(), false); 660 auto Result = callback(callback_baton, Status->type(), Spec); 661 if (Result == eEnumerateDirectoryResultQuit) 662 return; 663 if (Result == eEnumerateDirectoryResultNext) { 664 // Default behavior is to recurse. Opt out if the callback doesn't want 665 // this behavior. 666 Iter.no_push(); 667 } 668 } 669 } 670 671 FileSpec 672 FileSpec::CopyByAppendingPathComponent(llvm::StringRef component) const { 673 FileSpec ret = *this; 674 ret.AppendPathComponent(component); 675 return ret; 676 } 677 678 FileSpec FileSpec::CopyByRemovingLastPathComponent() const { 679 // CLEANUP: Use StringRef for string handling. 680 const bool resolve = false; 681 if (m_filename.IsEmpty() && m_directory.IsEmpty()) 682 return FileSpec("", resolve); 683 if (m_directory.IsEmpty()) 684 return FileSpec("", resolve); 685 if (m_filename.IsEmpty()) { 686 const char *dir_cstr = m_directory.GetCString(); 687 const char *last_slash_ptr = ::strrchr(dir_cstr, '/'); 688 689 // check for obvious cases before doing the full thing 690 if (!last_slash_ptr) 691 return FileSpec("", resolve); 692 if (last_slash_ptr == dir_cstr) 693 return FileSpec("/", resolve); 694 695 size_t last_slash_pos = last_slash_ptr - dir_cstr + 1; 696 ConstString new_path(dir_cstr, last_slash_pos); 697 return FileSpec(new_path.GetCString(), resolve); 698 } else 699 return FileSpec(m_directory.GetCString(), resolve); 700 } 701 702 ConstString FileSpec::GetLastPathComponent() const { 703 // CLEANUP: Use StringRef for string handling. 704 if (m_filename) 705 return m_filename; 706 if (m_directory) { 707 const char *dir_cstr = m_directory.GetCString(); 708 const char *last_slash_ptr = ::strrchr(dir_cstr, '/'); 709 if (last_slash_ptr == NULL) 710 return m_directory; 711 if (last_slash_ptr == dir_cstr) { 712 if (last_slash_ptr[1] == 0) 713 return ConstString(last_slash_ptr); 714 else 715 return ConstString(last_slash_ptr + 1); 716 } 717 if (last_slash_ptr[1] != 0) 718 return ConstString(last_slash_ptr + 1); 719 const char *penultimate_slash_ptr = last_slash_ptr; 720 while (*penultimate_slash_ptr) { 721 --penultimate_slash_ptr; 722 if (penultimate_slash_ptr == dir_cstr) 723 break; 724 if (*penultimate_slash_ptr == '/') 725 break; 726 } 727 ConstString result(penultimate_slash_ptr + 1, 728 last_slash_ptr - penultimate_slash_ptr); 729 return result; 730 } 731 return ConstString(); 732 } 733 734 static std::string 735 join_path_components(FileSpec::Style style, 736 const std::vector<llvm::StringRef> components) { 737 std::string result; 738 for (size_t i = 0; i < components.size(); ++i) { 739 if (components[i].empty()) 740 continue; 741 result += components[i]; 742 if (i != components.size() - 1 && 743 !IsPathSeparator(components[i].back(), style)) 744 result += GetPreferredPathSeparator(style); 745 } 746 747 return result; 748 } 749 750 void FileSpec::PrependPathComponent(llvm::StringRef component) { 751 if (component.empty()) 752 return; 753 754 const bool resolve = false; 755 if (m_filename.IsEmpty() && m_directory.IsEmpty()) { 756 SetFile(component, resolve); 757 return; 758 } 759 760 std::string result = 761 join_path_components(m_style, {component, m_directory.GetStringRef(), 762 m_filename.GetStringRef()}); 763 SetFile(result, resolve, m_style); 764 } 765 766 void FileSpec::PrependPathComponent(const FileSpec &new_path) { 767 return PrependPathComponent(new_path.GetPath(false)); 768 } 769 770 void FileSpec::AppendPathComponent(llvm::StringRef component) { 771 if (component.empty()) 772 return; 773 774 component = component.drop_while( 775 [this](char c) { return IsPathSeparator(c, m_style); }); 776 777 std::string result = 778 join_path_components(m_style, {m_directory.GetStringRef(), 779 m_filename.GetStringRef(), component}); 780 781 SetFile(result, false, m_style); 782 } 783 784 void FileSpec::AppendPathComponent(const FileSpec &new_path) { 785 return AppendPathComponent(new_path.GetPath(false)); 786 } 787 788 bool FileSpec::RemoveLastPathComponent() { 789 llvm::SmallString<64> current_path; 790 GetPath(current_path, false); 791 if (llvm::sys::path::has_parent_path(current_path, m_style)) { 792 SetFile(llvm::sys::path::parent_path(current_path, m_style), false, 793 m_style); 794 return true; 795 } 796 return false; 797 } 798 //------------------------------------------------------------------ 799 /// Returns true if the filespec represents an implementation source 800 /// file (files with a ".c", ".cpp", ".m", ".mm" (many more) 801 /// extension). 802 /// 803 /// @return 804 /// \b true if the filespec represents an implementation source 805 /// file, \b false otherwise. 806 //------------------------------------------------------------------ 807 bool FileSpec::IsSourceImplementationFile() const { 808 ConstString extension(GetFileNameExtension()); 809 if (!extension) 810 return false; 811 812 static RegularExpression g_source_file_regex(llvm::StringRef( 813 "^([cC]|[mM]|[mM][mM]|[cC][pP][pP]|[cC]\\+\\+|[cC][xX][xX]|[cC][cC]|[" 814 "cC][pP]|[sS]|[aA][sS][mM]|[fF]|[fF]77|[fF]90|[fF]95|[fF]03|[fF][oO][" 815 "rR]|[fF][tT][nN]|[fF][pP][pP]|[aA][dD][aA]|[aA][dD][bB]|[aA][dD][sS])" 816 "$")); 817 return g_source_file_regex.Execute(extension.GetStringRef()); 818 } 819 820 bool FileSpec::IsRelative() const { 821 if (m_directory) 822 return PathIsRelative(m_directory.GetStringRef(), m_style); 823 else 824 return PathIsRelative(m_filename.GetStringRef(), m_style); 825 } 826 827 bool FileSpec::IsAbsolute() const { return !FileSpec::IsRelative(); } 828 829 void llvm::format_provider<FileSpec>::format(const FileSpec &F, 830 raw_ostream &Stream, 831 StringRef Style) { 832 assert( 833 (Style.empty() || Style.equals_lower("F") || Style.equals_lower("D")) && 834 "Invalid FileSpec style!"); 835 836 StringRef dir = F.GetDirectory().GetStringRef(); 837 StringRef file = F.GetFilename().GetStringRef(); 838 839 if (dir.empty() && file.empty()) { 840 Stream << "(empty)"; 841 return; 842 } 843 844 if (Style.equals_lower("F")) { 845 Stream << (file.empty() ? "(empty)" : file); 846 return; 847 } 848 849 // Style is either D or empty, either way we need to print the directory. 850 if (!dir.empty()) { 851 // Directory is stored in normalized form, which might be different than 852 // preferred form. In order to handle this, we need to cut off the 853 // filename, then denormalize, then write the entire denorm'ed directory. 854 llvm::SmallString<64> denormalized_dir = dir; 855 Denormalize(denormalized_dir, F.GetPathStyle()); 856 Stream << denormalized_dir; 857 Stream << GetPreferredPathSeparator(F.GetPathStyle()); 858 } 859 860 if (Style.equals_lower("D")) { 861 // We only want to print the directory, so now just exit. 862 if (dir.empty()) 863 Stream << "(empty)"; 864 return; 865 } 866 867 if (!file.empty()) 868 Stream << file; 869 } 870