FileSpec.cpp revision 82cfaed47126a17b4fa21e42e05546e7a041b9e3
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 11#include <dirent.h> 12#include <fcntl.h> 13#include <libgen.h> 14#include <sys/stat.h> 15#include <string.h> 16#include <fstream> 17 18#include "lldb/Host/Config.h" // Have to include this before we test the define... 19#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER 20#include <pwd.h> 21#endif 22 23#include "llvm/ADT/StringRef.h" 24#include "llvm/Support/Path.h" 25#include "llvm/Support/Program.h" 26 27#include "lldb/Host/FileSpec.h" 28#include "lldb/Core/DataBufferHeap.h" 29#include "lldb/Core/DataBufferMemoryMap.h" 30#include "lldb/Core/Stream.h" 31#include "lldb/Host/Host.h" 32#include "lldb/Utility/CleanUp.h" 33 34using namespace lldb; 35using namespace lldb_private; 36using namespace std; 37 38static bool 39GetFileStats (const FileSpec *file_spec, struct stat *stats_ptr) 40{ 41 char resolved_path[PATH_MAX]; 42 if (file_spec->GetPath(&resolved_path[0], sizeof(resolved_path))) 43 return ::stat (resolved_path, stats_ptr) == 0; 44 return false; 45} 46 47#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER 48 49static const char* 50GetCachedGlobTildeSlash() 51{ 52 static std::string g_tilde; 53 if (g_tilde.empty()) 54 { 55 struct passwd *user_entry; 56 user_entry = getpwuid(geteuid()); 57 if (user_entry != NULL) 58 g_tilde = user_entry->pw_dir; 59 60 if (g_tilde.empty()) 61 return NULL; 62 } 63 return g_tilde.c_str(); 64} 65 66#endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER 67 68// Resolves the username part of a path of the form ~user/other/directories, and 69// writes the result into dst_path. 70// Returns 0 if there WAS a ~ in the path but the username couldn't be resolved. 71// Otherwise returns the number of characters copied into dst_path. If the return 72// is >= dst_len, then the resolved path is too long... 73size_t 74FileSpec::ResolveUsername (const char *src_path, char *dst_path, size_t dst_len) 75{ 76 if (src_path == NULL || src_path[0] == '\0') 77 return 0; 78 79#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER 80 81 char user_home[PATH_MAX]; 82 const char *user_name; 83 84 85 // If there's no ~, then just copy src_path straight to dst_path (they may be the same string...) 86 if (src_path[0] != '~') 87 { 88 size_t len = strlen (src_path); 89 if (len >= dst_len) 90 { 91 ::bcopy (src_path, dst_path, dst_len - 1); 92 dst_path[dst_len] = '\0'; 93 } 94 else 95 ::bcopy (src_path, dst_path, len + 1); 96 97 return len; 98 } 99 100 const char *first_slash = ::strchr (src_path, '/'); 101 char remainder[PATH_MAX]; 102 103 if (first_slash == NULL) 104 { 105 // The whole name is the username (minus the ~): 106 user_name = src_path + 1; 107 remainder[0] = '\0'; 108 } 109 else 110 { 111 int user_name_len = first_slash - src_path - 1; 112 ::memcpy (user_home, src_path + 1, user_name_len); 113 user_home[user_name_len] = '\0'; 114 user_name = user_home; 115 116 ::strcpy (remainder, first_slash); 117 } 118 119 if (user_name == NULL) 120 return 0; 121 // User name of "" means the current user... 122 123 struct passwd *user_entry; 124 const char *home_dir = NULL; 125 126 if (user_name[0] == '\0') 127 { 128 home_dir = GetCachedGlobTildeSlash(); 129 } 130 else 131 { 132 user_entry = ::getpwnam (user_name); 133 if (user_entry != NULL) 134 home_dir = user_entry->pw_dir; 135 } 136 137 if (home_dir == NULL) 138 return 0; 139 else 140 return ::snprintf (dst_path, dst_len, "%s%s", home_dir, remainder); 141#else 142 // Resolving home directories is not supported, just copy the path... 143 return ::snprintf (dst_path, dst_len, "%s", src_path); 144#endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER 145} 146 147size_t 148FileSpec::Resolve (const char *src_path, char *dst_path, size_t dst_len) 149{ 150 if (src_path == NULL || src_path[0] == '\0') 151 return 0; 152 153 // Glob if needed for ~/, otherwise copy in case src_path is same as dst_path... 154 char unglobbed_path[PATH_MAX]; 155#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER 156 if (src_path[0] == '~') 157 { 158 size_t return_count = ResolveUsername(src_path, unglobbed_path, sizeof(unglobbed_path)); 159 160 // If we couldn't find the user referred to, or the resultant path was too long, 161 // then just copy over the src_path. 162 if (return_count == 0 || return_count >= sizeof(unglobbed_path)) 163 ::snprintf (unglobbed_path, sizeof(unglobbed_path), "%s", src_path); 164 } 165 else 166#endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER 167 { 168 ::snprintf(unglobbed_path, sizeof(unglobbed_path), "%s", src_path); 169 } 170 171 // Now resolve the path if needed 172 char resolved_path[PATH_MAX]; 173 if (::realpath (unglobbed_path, resolved_path)) 174 { 175 // Success, copy the resolved path 176 return ::snprintf(dst_path, dst_len, "%s", resolved_path); 177 } 178 else 179 { 180 // Failed, just copy the unglobbed path 181 return ::snprintf(dst_path, dst_len, "%s", unglobbed_path); 182 } 183} 184 185FileSpec::FileSpec() : 186 m_directory(), 187 m_filename() 188{ 189} 190 191//------------------------------------------------------------------ 192// Default constructor that can take an optional full path to a 193// file on disk. 194//------------------------------------------------------------------ 195FileSpec::FileSpec(const char *pathname, bool resolve_path) : 196 m_directory(), 197 m_filename(), 198 m_is_resolved(false) 199{ 200 if (pathname && pathname[0]) 201 SetFile(pathname, resolve_path); 202} 203 204//------------------------------------------------------------------ 205// Copy constructor 206//------------------------------------------------------------------ 207FileSpec::FileSpec(const FileSpec& rhs) : 208 m_directory (rhs.m_directory), 209 m_filename (rhs.m_filename), 210 m_is_resolved (rhs.m_is_resolved) 211{ 212} 213 214//------------------------------------------------------------------ 215// Copy constructor 216//------------------------------------------------------------------ 217FileSpec::FileSpec(const FileSpec* rhs) : 218 m_directory(), 219 m_filename() 220{ 221 if (rhs) 222 *this = *rhs; 223} 224 225//------------------------------------------------------------------ 226// Virtual destrcuctor in case anyone inherits from this class. 227//------------------------------------------------------------------ 228FileSpec::~FileSpec() 229{ 230} 231 232//------------------------------------------------------------------ 233// Assignment operator. 234//------------------------------------------------------------------ 235const FileSpec& 236FileSpec::operator= (const FileSpec& rhs) 237{ 238 if (this != &rhs) 239 { 240 m_directory = rhs.m_directory; 241 m_filename = rhs.m_filename; 242 m_is_resolved = rhs.m_is_resolved; 243 } 244 return *this; 245} 246 247//------------------------------------------------------------------ 248// Update the contents of this object with a new path. The path will 249// be split up into a directory and filename and stored as uniqued 250// string values for quick comparison and efficient memory usage. 251//------------------------------------------------------------------ 252void 253FileSpec::SetFile (const char *pathname, bool resolve) 254{ 255 m_filename.Clear(); 256 m_directory.Clear(); 257 m_is_resolved = false; 258 if (pathname == NULL || pathname[0] == '\0') 259 return; 260 261 char resolved_path[PATH_MAX]; 262 bool path_fit = true; 263 264 if (resolve) 265 { 266 path_fit = (FileSpec::Resolve (pathname, resolved_path, sizeof(resolved_path)) < sizeof(resolved_path) - 1); 267 m_is_resolved = path_fit; 268 } 269 else 270 { 271 // Copy the path because "basename" and "dirname" want to muck with the 272 // path buffer 273 if (::strlen (pathname) > sizeof(resolved_path) - 1) 274 path_fit = false; 275 else 276 ::strcpy (resolved_path, pathname); 277 } 278 279 280 if (path_fit) 281 { 282 char *filename = ::basename (resolved_path); 283 if (filename) 284 { 285 m_filename.SetCString (filename); 286 // Truncate the basename off the end of the resolved path 287 288 // Only attempt to get the dirname if it looks like we have a path 289 if (strchr(resolved_path, '/')) 290 { 291 char *directory = ::dirname (resolved_path); 292 293 // Make sure we didn't get our directory resolved to "." without having 294 // specified 295 if (directory) 296 m_directory.SetCString(directory); 297 else 298 { 299 char *last_resolved_path_slash = strrchr(resolved_path, '/'); 300 if (last_resolved_path_slash) 301 { 302 *last_resolved_path_slash = '\0'; 303 m_directory.SetCString(resolved_path); 304 } 305 } 306 } 307 } 308 else 309 m_directory.SetCString(resolved_path); 310 } 311} 312 313//---------------------------------------------------------------------- 314// Convert to pointer operator. This allows code to check any FileSpec 315// objects to see if they contain anything valid using code such as: 316// 317// if (file_spec) 318// {} 319//---------------------------------------------------------------------- 320FileSpec::operator 321void*() const 322{ 323 return (m_directory || m_filename) ? const_cast<FileSpec*>(this) : NULL; 324} 325 326//---------------------------------------------------------------------- 327// Logical NOT operator. This allows code to check any FileSpec 328// objects to see if they are invalid using code such as: 329// 330// if (!file_spec) 331// {} 332//---------------------------------------------------------------------- 333bool 334FileSpec::operator!() const 335{ 336 return !m_directory && !m_filename; 337} 338 339//------------------------------------------------------------------ 340// Equal to operator 341//------------------------------------------------------------------ 342bool 343FileSpec::operator== (const FileSpec& rhs) const 344{ 345 if (m_filename == rhs.m_filename) 346 { 347 if (m_directory == rhs.m_directory) 348 return true; 349 350 // TODO: determine if we want to keep this code in here. 351 // The code below was added to handle a case where we were 352 // trying to set a file and line breakpoint and one path 353 // was resolved, and the other not and the directory was 354 // in a mount point that resolved to a more complete path: 355 // "/tmp/a.c" == "/private/tmp/a.c". I might end up pulling 356 // this out... 357 if (IsResolved() && rhs.IsResolved()) 358 { 359 // Both paths are resolved, no need to look further... 360 return false; 361 } 362 363 FileSpec resolved_lhs(*this); 364 365 // If "this" isn't resolved, resolve it 366 if (!IsResolved()) 367 { 368 if (resolved_lhs.ResolvePath()) 369 { 370 // This path wasn't resolved but now it is. Check if the resolved 371 // directory is the same as our unresolved directory, and if so, 372 // we can mark this object as resolved to avoid more future resolves 373 m_is_resolved = (m_directory == resolved_lhs.m_directory); 374 } 375 else 376 return false; 377 } 378 379 FileSpec resolved_rhs(rhs); 380 if (!rhs.IsResolved()) 381 { 382 if (resolved_rhs.ResolvePath()) 383 { 384 // rhs's path wasn't resolved but now it is. Check if the resolved 385 // directory is the same as rhs's unresolved directory, and if so, 386 // we can mark this object as resolved to avoid more future resolves 387 rhs.m_is_resolved = (m_directory == resolved_rhs.m_directory); 388 } 389 else 390 return false; 391 } 392 393 // If we reach this point in the code we were able to resolve both paths 394 // and since we only resolve the paths if the basenames are equal, then 395 // we can just check if both directories are equal... 396 return resolved_lhs.GetDirectory() == resolved_rhs.GetDirectory(); 397 } 398 return false; 399} 400 401//------------------------------------------------------------------ 402// Not equal to operator 403//------------------------------------------------------------------ 404bool 405FileSpec::operator!= (const FileSpec& rhs) const 406{ 407 return !(*this == rhs); 408} 409 410//------------------------------------------------------------------ 411// Less than operator 412//------------------------------------------------------------------ 413bool 414FileSpec::operator< (const FileSpec& rhs) const 415{ 416 return FileSpec::Compare(*this, rhs, true) < 0; 417} 418 419//------------------------------------------------------------------ 420// Dump a FileSpec object to a stream 421//------------------------------------------------------------------ 422Stream& 423lldb_private::operator << (Stream &s, const FileSpec& f) 424{ 425 f.Dump(&s); 426 return s; 427} 428 429//------------------------------------------------------------------ 430// Clear this object by releasing both the directory and filename 431// string values and making them both the empty string. 432//------------------------------------------------------------------ 433void 434FileSpec::Clear() 435{ 436 m_directory.Clear(); 437 m_filename.Clear(); 438} 439 440//------------------------------------------------------------------ 441// Compare two FileSpec objects. If "full" is true, then both 442// the directory and the filename must match. If "full" is false, 443// then the directory names for "a" and "b" are only compared if 444// they are both non-empty. This allows a FileSpec object to only 445// contain a filename and it can match FileSpec objects that have 446// matching filenames with different paths. 447// 448// Return -1 if the "a" is less than "b", 0 if "a" is equal to "b" 449// and "1" if "a" is greater than "b". 450//------------------------------------------------------------------ 451int 452FileSpec::Compare(const FileSpec& a, const FileSpec& b, bool full) 453{ 454 int result = 0; 455 456 // If full is true, then we must compare both the directory and filename. 457 458 // If full is false, then if either directory is empty, then we match on 459 // the basename only, and if both directories have valid values, we still 460 // do a full compare. This allows for matching when we just have a filename 461 // in one of the FileSpec objects. 462 463 if (full || (a.m_directory && b.m_directory)) 464 { 465 result = ConstString::Compare(a.m_directory, b.m_directory); 466 if (result) 467 return result; 468 } 469 return ConstString::Compare (a.m_filename, b.m_filename); 470} 471 472bool 473FileSpec::Equal (const FileSpec& a, const FileSpec& b, bool full) 474{ 475 if (full) 476 return a == b; 477 else 478 return a.m_filename == b.m_filename; 479} 480 481 482 483//------------------------------------------------------------------ 484// Dump the object to the supplied stream. If the object contains 485// a valid directory name, it will be displayed followed by a 486// directory delimiter, and the filename. 487//------------------------------------------------------------------ 488void 489FileSpec::Dump(Stream *s) const 490{ 491 if (m_filename) 492 m_directory.Dump(s, ""); // Provide a default for m_directory when we dump it in case it is invalid 493 494 if (m_directory) 495 { 496 // If dirname was valid, then we need to print a slash between 497 // the directory and the filename 498 s->PutChar('/'); 499 } 500 m_filename.Dump(s); 501} 502 503//------------------------------------------------------------------ 504// Returns true if the file exists. 505//------------------------------------------------------------------ 506bool 507FileSpec::Exists () const 508{ 509 struct stat file_stats; 510 return GetFileStats (this, &file_stats); 511} 512 513bool 514FileSpec::ResolveExecutableLocation () 515{ 516 if (!m_directory) 517 { 518 const char *file_cstr = m_filename.GetCString(); 519 if (file_cstr) 520 { 521 const std::string file_str (file_cstr); 522 llvm::sys::Path path = llvm::sys::Program::FindProgramByName (file_str); 523 const std::string &path_str = path.str(); 524 llvm::StringRef dir_ref = llvm::sys::path::parent_path(path_str); 525 //llvm::StringRef dir_ref = path.getDirname(); 526 if (! dir_ref.empty()) 527 { 528 // FindProgramByName returns "." if it can't find the file. 529 if (strcmp (".", dir_ref.data()) == 0) 530 return false; 531 532 m_directory.SetCString (dir_ref.data()); 533 if (Exists()) 534 return true; 535 else 536 { 537 // If FindProgramByName found the file, it returns the directory + filename in its return results. 538 // We need to separate them. 539 FileSpec tmp_file (dir_ref.data(), false); 540 if (tmp_file.Exists()) 541 { 542 m_directory = tmp_file.m_directory; 543 return true; 544 } 545 } 546 } 547 } 548 } 549 550 return false; 551} 552 553bool 554FileSpec::ResolvePath () 555{ 556 if (m_is_resolved) 557 return true; // We have already resolved this path 558 559 char path_buf[PATH_MAX]; 560 if (!GetPath (path_buf, PATH_MAX)) 561 return false; 562 // SetFile(...) will set m_is_resolved correctly if it can resolve the path 563 SetFile (path_buf, true); 564 return m_is_resolved; 565} 566 567uint64_t 568FileSpec::GetByteSize() const 569{ 570 struct stat file_stats; 571 if (GetFileStats (this, &file_stats)) 572 return file_stats.st_size; 573 return 0; 574} 575 576FileSpec::FileType 577FileSpec::GetFileType () const 578{ 579 struct stat file_stats; 580 if (GetFileStats (this, &file_stats)) 581 { 582 mode_t file_type = file_stats.st_mode & S_IFMT; 583 switch (file_type) 584 { 585 case S_IFDIR: return eFileTypeDirectory; 586 case S_IFIFO: return eFileTypePipe; 587 case S_IFREG: return eFileTypeRegular; 588 case S_IFSOCK: return eFileTypeSocket; 589 case S_IFLNK: return eFileTypeSymbolicLink; 590 default: 591 break; 592 } 593 return eFileTypeUnknown; 594 } 595 return eFileTypeInvalid; 596} 597 598TimeValue 599FileSpec::GetModificationTime () const 600{ 601 TimeValue mod_time; 602 struct stat file_stats; 603 if (GetFileStats (this, &file_stats)) 604 mod_time.OffsetWithSeconds(file_stats.st_mtime); 605 return mod_time; 606} 607 608//------------------------------------------------------------------ 609// Directory string get accessor. 610//------------------------------------------------------------------ 611ConstString & 612FileSpec::GetDirectory() 613{ 614 return m_directory; 615} 616 617//------------------------------------------------------------------ 618// Directory string const get accessor. 619//------------------------------------------------------------------ 620const ConstString & 621FileSpec::GetDirectory() const 622{ 623 return m_directory; 624} 625 626//------------------------------------------------------------------ 627// Filename string get accessor. 628//------------------------------------------------------------------ 629ConstString & 630FileSpec::GetFilename() 631{ 632 return m_filename; 633} 634 635//------------------------------------------------------------------ 636// Filename string const get accessor. 637//------------------------------------------------------------------ 638const ConstString & 639FileSpec::GetFilename() const 640{ 641 return m_filename; 642} 643 644//------------------------------------------------------------------ 645// Extract the directory and path into a fixed buffer. This is 646// needed as the directory and path are stored in separate string 647// values. 648//------------------------------------------------------------------ 649size_t 650FileSpec::GetPath(char *path, size_t path_max_len) const 651{ 652 if (path_max_len) 653 { 654 const char *dirname = m_directory.GetCString(); 655 const char *filename = m_filename.GetCString(); 656 if (dirname) 657 { 658 if (filename) 659 return ::snprintf (path, path_max_len, "%s/%s", dirname, filename); 660 else 661 return ::snprintf (path, path_max_len, "%s", dirname); 662 } 663 else if (filename) 664 { 665 return ::snprintf (path, path_max_len, "%s", filename); 666 } 667 } 668 path[0] = '\0'; 669 return 0; 670} 671 672//------------------------------------------------------------------ 673// Returns a shared pointer to a data buffer that contains all or 674// part of the contents of a file. The data is memory mapped and 675// will lazily page in data from the file as memory is accessed. 676// The data that is mappped will start "file_offset" bytes into the 677// file, and "file_size" bytes will be mapped. If "file_size" is 678// greater than the number of bytes available in the file starting 679// at "file_offset", the number of bytes will be appropriately 680// truncated. The final number of bytes that get mapped can be 681// verified using the DataBuffer::GetByteSize() function. 682//------------------------------------------------------------------ 683DataBufferSP 684FileSpec::MemoryMapFileContents(off_t file_offset, size_t file_size) const 685{ 686 DataBufferSP data_sp; 687 auto_ptr<DataBufferMemoryMap> mmap_data(new DataBufferMemoryMap()); 688 if (mmap_data.get()) 689 { 690 if (mmap_data->MemoryMapFromFileSpec (this, file_offset, file_size) >= file_size) 691 data_sp.reset(mmap_data.release()); 692 } 693 return data_sp; 694} 695 696 697//------------------------------------------------------------------ 698// Return the size in bytes that this object takes in memory. This 699// returns the size in bytes of this object, not any shared string 700// values it may refer to. 701//------------------------------------------------------------------ 702size_t 703FileSpec::MemorySize() const 704{ 705 return m_filename.MemorySize() + m_directory.MemorySize(); 706} 707 708 709size_t 710FileSpec::ReadFileContents (off_t file_offset, void *dst, size_t dst_len) const 711{ 712 size_t bytes_read = 0; 713 char resolved_path[PATH_MAX]; 714 if (GetPath(resolved_path, sizeof(resolved_path))) 715 { 716 int fd = ::open (resolved_path, O_RDONLY, 0); 717 if (fd != -1) 718 { 719 struct stat file_stats; 720 if (::fstat (fd, &file_stats) == 0) 721 { 722 // Read bytes directly into our basic_string buffer 723 if (file_stats.st_size > 0) 724 { 725 off_t lseek_result = 0; 726 if (file_offset > 0) 727 lseek_result = ::lseek (fd, file_offset, SEEK_SET); 728 729 if (lseek_result == file_offset) 730 { 731 ssize_t n = ::read (fd, dst, dst_len); 732 if (n >= 0) 733 bytes_read = n; 734 } 735 } 736 } 737 } 738 close(fd); 739 } 740 return bytes_read; 741} 742 743//------------------------------------------------------------------ 744// Returns a shared pointer to a data buffer that contains all or 745// part of the contents of a file. The data copies into a heap based 746// buffer that lives in the DataBuffer shared pointer object returned. 747// The data that is cached will start "file_offset" bytes into the 748// file, and "file_size" bytes will be mapped. If "file_size" is 749// greater than the number of bytes available in the file starting 750// at "file_offset", the number of bytes will be appropriately 751// truncated. The final number of bytes that get mapped can be 752// verified using the DataBuffer::GetByteSize() function. 753//------------------------------------------------------------------ 754DataBufferSP 755FileSpec::ReadFileContents (off_t file_offset, size_t file_size) const 756{ 757 DataBufferSP data_sp; 758 char resolved_path[PATH_MAX]; 759 if (GetPath(resolved_path, sizeof(resolved_path))) 760 { 761 int fd = ::open (resolved_path, O_RDONLY, 0); 762 if (fd != -1) 763 { 764 struct stat file_stats; 765 if (::fstat (fd, &file_stats) == 0) 766 { 767 if (file_stats.st_size > 0) 768 { 769 off_t lseek_result = 0; 770 if (file_offset > 0) 771 lseek_result = ::lseek (fd, file_offset, SEEK_SET); 772 773 if (lseek_result < 0) 774 { 775 // Get error from errno 776 } 777 else if (lseek_result == file_offset) 778 { 779 const size_t bytes_left = file_stats.st_size - file_offset; 780 size_t num_bytes_to_read = file_size; 781 if (num_bytes_to_read > bytes_left) 782 num_bytes_to_read = bytes_left; 783 784 std::auto_ptr<DataBufferHeap> data_heap_ap; 785 data_heap_ap.reset(new DataBufferHeap(num_bytes_to_read, '\0')); 786 787 if (data_heap_ap.get()) 788 { 789 ssize_t bytesRead = ::read (fd, (void *)data_heap_ap->GetBytes(), data_heap_ap->GetByteSize()); 790 if (bytesRead >= 0) 791 { 792 // Make sure we read exactly what we asked for and if we got 793 // less, adjust the array 794 if ((size_t)bytesRead < data_heap_ap->GetByteSize()) 795 data_heap_ap->SetByteSize(bytesRead); 796 data_sp.reset(data_heap_ap.release()); 797 } 798 } 799 } 800 } 801 } 802 } 803 close(fd); 804 } 805 return data_sp; 806} 807 808size_t 809FileSpec::ReadFileLines (STLStringArray &lines) 810{ 811 lines.clear(); 812 char path[PATH_MAX]; 813 if (GetPath(path, sizeof(path))) 814 { 815 ifstream file_stream (path); 816 817 if (file_stream) 818 { 819 std::string line; 820 while (getline (file_stream, line)) 821 lines.push_back (line); 822 } 823 } 824 return lines.size(); 825} 826 827FileSpec::EnumerateDirectoryResult 828FileSpec::EnumerateDirectory 829( 830 const char *dir_path, 831 bool find_directories, 832 bool find_files, 833 bool find_other, 834 EnumerateDirectoryCallbackType callback, 835 void *callback_baton 836) 837{ 838 if (dir_path && dir_path[0]) 839 { 840 lldb_utility::CleanUp <DIR *, int> dir_path_dir (opendir(dir_path), NULL, closedir); 841 if (dir_path_dir.is_valid()) 842 { 843 struct dirent* dp; 844 while ((dp = readdir(dir_path_dir.get())) != NULL) 845 { 846 // Only search directories 847 if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN) 848 { 849 size_t len = strlen(dp->d_name); 850 851 if (len == 1 && dp->d_name[0] == '.') 852 continue; 853 854 if (len == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.') 855 continue; 856 } 857 858 bool call_callback = false; 859 FileSpec::FileType file_type = eFileTypeUnknown; 860 861 switch (dp->d_type) 862 { 863 default: 864 case DT_UNKNOWN: file_type = eFileTypeUnknown; call_callback = true; break; 865 case DT_FIFO: file_type = eFileTypePipe; call_callback = find_other; break; 866 case DT_CHR: file_type = eFileTypeOther; call_callback = find_other; break; 867 case DT_DIR: file_type = eFileTypeDirectory; call_callback = find_directories; break; 868 case DT_BLK: file_type = eFileTypeOther; call_callback = find_other; break; 869 case DT_REG: file_type = eFileTypeRegular; call_callback = find_files; break; 870 case DT_LNK: file_type = eFileTypeSymbolicLink; call_callback = find_other; break; 871 case DT_SOCK: file_type = eFileTypeSocket; call_callback = find_other; break; 872 case DT_WHT: file_type = eFileTypeOther; call_callback = find_other; break; 873 } 874 875 if (call_callback) 876 { 877 char child_path[PATH_MAX]; 878 const int child_path_len = ::snprintf (child_path, sizeof(child_path), "%s/%s", dir_path, dp->d_name); 879 if (child_path_len < sizeof(child_path) - 1) 880 { 881 // Don't resolve the file type or path 882 FileSpec child_path_spec (child_path, false); 883 884 EnumerateDirectoryResult result = callback (callback_baton, file_type, child_path_spec); 885 886 switch (result) 887 { 888 default: 889 case eEnumerateDirectoryResultNext: 890 // Enumerate next entry in the current directory. We just 891 // exit this switch and will continue enumerating the 892 // current directory as we currently are... 893 break; 894 895 case eEnumerateDirectoryResultEnter: // Recurse into the current entry if it is a directory or symlink, or next if not 896 if (FileSpec::EnumerateDirectory (child_path, 897 find_directories, 898 find_files, 899 find_other, 900 callback, 901 callback_baton) == eEnumerateDirectoryResultQuit) 902 { 903 // The subdirectory returned Quit, which means to 904 // stop all directory enumerations at all levels. 905 return eEnumerateDirectoryResultQuit; 906 } 907 break; 908 909 case eEnumerateDirectoryResultExit: // Exit from the current directory at the current level. 910 // Exit from this directory level and tell parent to 911 // keep enumerating. 912 return eEnumerateDirectoryResultNext; 913 914 case eEnumerateDirectoryResultQuit: // Stop directory enumerations at any level 915 return eEnumerateDirectoryResultQuit; 916 } 917 } 918 } 919 } 920 } 921 } 922 // By default when exiting a directory, we tell the parent enumeration 923 // to continue enumerating. 924 return eEnumerateDirectoryResultNext; 925} 926 927