FileSpec.cpp revision 704363531ee4877ccc6d35d0702876096f54c67b
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 <fcntl.h> 12#include <glob.h> 13#include <libgen.h> 14#include <stdlib.h> 15#include <sys/param.h> 16#include <sys/stat.h> 17#include <sys/types.h> 18 19#include <fstream> 20 21#include "lldb/Core/FileSpec.h" 22#include "lldb/Core/DataBufferHeap.h" 23#include "lldb/Core/DataBufferMemoryMap.h" 24#include "lldb/Core/Stream.h" 25 26using namespace lldb; 27using namespace lldb_private; 28using namespace std; 29 30static bool 31GetFileStats (const FileSpec *file_spec, struct stat *stats_ptr) 32{ 33 char resolved_path[PATH_MAX]; 34 if (file_spec->GetPath(&resolved_path[0], sizeof(resolved_path))) 35 return ::stat (resolved_path, stats_ptr) == 0; 36 return false; 37} 38 39static const char* 40GetCachedGlobTildeSlash() 41{ 42 static std::string g_tilde; 43 if (g_tilde.empty()) 44 { 45 glob_t globbuf; 46 if (::glob("~/", GLOB_TILDE, NULL, &globbuf) == 0) //success 47 { 48 g_tilde = globbuf.gl_pathv[0]; 49 ::globfree (&globbuf); 50 } 51 if (g_tilde.empty()) 52 return NULL; 53 } 54 return g_tilde.c_str(); 55} 56 57int 58FileSpec::Resolve (const char *src_path, char *dst_path, size_t dst_len) 59{ 60 if (src_path == NULL || src_path[0] == '\0') 61 return 0; 62 63 // Glob if needed for ~/, otherwise copy in case src_path is same as dst_path... 64 char unglobbed_path[PATH_MAX]; 65 if (::strstr (src_path, "~/") == src_path) 66 ::snprintf(unglobbed_path, sizeof(unglobbed_path), "%s%s", GetCachedGlobTildeSlash(), src_path + 2); 67 else 68 ::snprintf(unglobbed_path, sizeof(unglobbed_path), "%s", src_path); 69 70 // Now resolve the path if needed 71 char resolved_path[PATH_MAX]; 72 if (::realpath (unglobbed_path, resolved_path)) 73 { 74 // Success, copy the resolved path 75 return ::snprintf(dst_path, dst_len, "%s", resolved_path); 76 } 77 else 78 { 79 // Failed, just copy the unglobbed path 80 return ::snprintf(dst_path, dst_len, "%s", unglobbed_path); 81 } 82} 83 84FileSpec::FileSpec() : 85 m_directory(), 86 m_filename() 87{ 88} 89 90//------------------------------------------------------------------ 91// Default constructor that can take an optional full path to a 92// file on disk. 93//------------------------------------------------------------------ 94FileSpec::FileSpec(const char *pathname) : 95 m_directory(), 96 m_filename() 97{ 98 if (pathname && pathname[0]) 99 SetFile(pathname); 100} 101 102//------------------------------------------------------------------ 103// Copy constructor 104//------------------------------------------------------------------ 105FileSpec::FileSpec(const FileSpec& rhs) : 106 m_directory (rhs.m_directory), 107 m_filename (rhs.m_filename) 108{ 109} 110 111//------------------------------------------------------------------ 112// Copy constructor 113//------------------------------------------------------------------ 114FileSpec::FileSpec(const FileSpec* rhs) : 115 m_directory(), 116 m_filename() 117{ 118 if (rhs) 119 *this = *rhs; 120} 121 122//------------------------------------------------------------------ 123// Virtual destrcuctor in case anyone inherits from this class. 124//------------------------------------------------------------------ 125FileSpec::~FileSpec() 126{ 127} 128 129//------------------------------------------------------------------ 130// Assignment operator. 131//------------------------------------------------------------------ 132const FileSpec& 133FileSpec::operator= (const FileSpec& rhs) 134{ 135 if (this != &rhs) 136 { 137 m_directory = rhs.m_directory; 138 m_filename = rhs.m_filename; 139 } 140 return *this; 141} 142 143 144//------------------------------------------------------------------ 145// Update the contents of this object with a new path. The path will 146// be split up into a directory and filename and stored as uniqued 147// string values for quick comparison and efficient memory usage. 148//------------------------------------------------------------------ 149void 150FileSpec::SetFile(const char *pathname) 151{ 152 m_filename.Clear(); 153 m_directory.Clear(); 154 if (pathname == NULL || pathname[0] == '\0') 155 return; 156 157 char resolved_path[PATH_MAX]; 158 159 if (FileSpec::Resolve (pathname, resolved_path, sizeof(resolved_path)) < sizeof(resolved_path) - 1) 160 { 161 char *filename = ::basename (resolved_path); 162 if (filename) 163 { 164 m_filename.SetCString (filename); 165 // Truncate the basename off the end of the resolved path 166 167 // Only attempt to get the dirname if it looks like we have a path 168 if (strchr(resolved_path, '/')) 169 { 170 char *directory = ::dirname (resolved_path); 171 172 // Make sure we didn't get our directory resolved to "." without having 173 // specified 174 if (directory) 175 m_directory.SetCString(directory); 176 else 177 { 178 char *last_resolved_path_slash = strrchr(resolved_path, '/'); 179 if (last_resolved_path_slash) 180 { 181 *last_resolved_path_slash = '\0'; 182 m_directory.SetCString(resolved_path); 183 } 184 } 185 } 186 } 187 else 188 m_directory.SetCString(resolved_path); 189 } 190} 191 192//---------------------------------------------------------------------- 193// Convert to pointer operator. This allows code to check any FileSpec 194// objects to see if they contain anything valid using code such as: 195// 196// if (file_spec) 197// {} 198//---------------------------------------------------------------------- 199FileSpec::operator 200void*() const 201{ 202 return (m_directory || m_filename) ? const_cast<FileSpec*>(this) : NULL; 203} 204 205//---------------------------------------------------------------------- 206// Logical NOT operator. This allows code to check any FileSpec 207// objects to see if they are invalid using code such as: 208// 209// if (!file_spec) 210// {} 211//---------------------------------------------------------------------- 212bool 213FileSpec::operator!() const 214{ 215 return !m_directory && !m_filename; 216} 217 218//------------------------------------------------------------------ 219// Equal to operator 220//------------------------------------------------------------------ 221bool 222FileSpec::operator== (const FileSpec& rhs) const 223{ 224 return m_directory == rhs.m_directory && m_filename == rhs.m_filename; 225} 226 227//------------------------------------------------------------------ 228// Not equal to operator 229//------------------------------------------------------------------ 230bool 231FileSpec::operator!= (const FileSpec& rhs) const 232{ 233 return m_filename != rhs.m_filename || m_directory != rhs.m_directory; 234} 235 236//------------------------------------------------------------------ 237// Less than operator 238//------------------------------------------------------------------ 239bool 240FileSpec::operator< (const FileSpec& rhs) const 241{ 242 return FileSpec::Compare(*this, rhs, true) < 0; 243} 244 245//------------------------------------------------------------------ 246// Dump a FileSpec object to a stream 247//------------------------------------------------------------------ 248Stream& 249lldb_private::operator << (Stream &s, const FileSpec& f) 250{ 251 f.Dump(&s); 252 return s; 253} 254 255//------------------------------------------------------------------ 256// Clear this object by releasing both the directory and filename 257// string values and making them both the empty string. 258//------------------------------------------------------------------ 259void 260FileSpec::Clear() 261{ 262 m_directory.Clear(); 263 m_filename.Clear(); 264} 265 266//------------------------------------------------------------------ 267// Compare two FileSpec objects. If "full" is true, then both 268// the directory and the filename must match. If "full" is false, 269// then the directory names for "a" and "b" are only compared if 270// they are both non-empty. This allows a FileSpec object to only 271// contain a filename and it can match FileSpec objects that have 272// matching filenames with different paths. 273// 274// Return -1 if the "a" is less than "b", 0 if "a" is equal to "b" 275// and "1" if "a" is greater than "b". 276//------------------------------------------------------------------ 277int 278FileSpec::Compare(const FileSpec& a, const FileSpec& b, bool full) 279{ 280 int result = 0; 281 282 // If full is true, then we must compare both the directory and filename. 283 284 // If full is false, then if either directory is empty, then we match on 285 // the basename only, and if both directories have valid values, we still 286 // do a full compare. This allows for matching when we just have a filename 287 // in one of the FileSpec objects. 288 289 if (full || (a.m_directory && b.m_directory)) 290 { 291 result = ConstString::Compare(a.m_directory, b.m_directory); 292 if (result) 293 return result; 294 } 295 return ConstString::Compare (a.m_filename, b.m_filename); 296} 297 298bool 299FileSpec::Equal (const FileSpec& a, const FileSpec& b, bool full) 300{ 301 if (full) 302 return a == b; 303 else 304 return a.m_filename == b.m_filename; 305} 306 307 308 309//------------------------------------------------------------------ 310// Dump the object to the supplied stream. If the object contains 311// a valid directory name, it will be displayed followed by a 312// directory delimiter, and the filename. 313//------------------------------------------------------------------ 314void 315FileSpec::Dump(Stream *s) const 316{ 317 if (m_filename) 318 m_directory.Dump(s, ""); // Provide a default for m_directory when we dump it in case it is invalid 319 320 if (m_directory) 321 { 322 // If dirname was valid, then we need to print a slash between 323 // the directory and the filename 324 s->PutChar('/'); 325 } 326 m_filename.Dump(s); 327} 328 329//------------------------------------------------------------------ 330// Returns true if the file exists. 331//------------------------------------------------------------------ 332bool 333FileSpec::Exists () const 334{ 335 struct stat file_stats; 336 return GetFileStats (this, &file_stats); 337} 338 339uint64_t 340FileSpec::GetByteSize() const 341{ 342 struct stat file_stats; 343 if (GetFileStats (this, &file_stats)) 344 return file_stats.st_size; 345 return 0; 346} 347 348FileSpec::FileType 349FileSpec::GetFileType () const 350{ 351 struct stat file_stats; 352 if (GetFileStats (this, &file_stats)) 353 { 354 mode_t file_type = file_stats.st_mode & S_IFMT; 355 switch (file_type) 356 { 357 case S_IFDIR: return eFileTypeDirectory; 358 case S_IFIFO: return eFileTypePipe; 359 case S_IFREG: return eFileTypeRegular; 360 case S_IFSOCK: return eFileTypeSocket; 361 case S_IFLNK: return eFileTypeSymbolicLink; 362 default: 363 break; 364 } 365 return eFileTypeUknown; 366 } 367 return eFileTypeInvalid; 368} 369 370TimeValue 371FileSpec::GetModificationTime () const 372{ 373 TimeValue mod_time; 374 struct stat file_stats; 375 if (GetFileStats (this, &file_stats)) 376 mod_time.OffsetWithSeconds(file_stats.st_mtime); 377 return mod_time; 378} 379 380//------------------------------------------------------------------ 381// Directory string get accessor. 382//------------------------------------------------------------------ 383ConstString & 384FileSpec::GetDirectory() 385{ 386 return m_directory; 387} 388 389//------------------------------------------------------------------ 390// Directory string const get accessor. 391//------------------------------------------------------------------ 392const ConstString & 393FileSpec::GetDirectory() const 394{ 395 return m_directory; 396} 397 398//------------------------------------------------------------------ 399// Filename string get accessor. 400//------------------------------------------------------------------ 401ConstString & 402FileSpec::GetFilename() 403{ 404 return m_filename; 405} 406 407//------------------------------------------------------------------ 408// Filename string const get accessor. 409//------------------------------------------------------------------ 410const ConstString & 411FileSpec::GetFilename() const 412{ 413 return m_filename; 414} 415 416//------------------------------------------------------------------ 417// Extract the directory and path into a fixed buffer. This is 418// needed as the directory and path are stored in separate string 419// values. 420//------------------------------------------------------------------ 421bool 422FileSpec::GetPath(char *path, size_t max_path_length) const 423{ 424 if (max_path_length == 0) 425 return false; 426 427 path[0] = '\0'; 428 const char *dirname = m_directory.AsCString(); 429 const char *filename = m_filename.AsCString(); 430 if (dirname) 431 { 432 if (filename && filename[0]) 433 { 434 return snprintf (path, max_path_length, "%s/%s", dirname, filename) < max_path_length; 435 } 436 else 437 { 438 strncpy (path, dirname, max_path_length); 439 } 440 } 441 else if (filename) 442 { 443 strncpy (path, filename, max_path_length); 444 } 445 else 446 { 447 return false; 448 } 449 450 // Any code paths that reach here assume that strncpy, or a similar function was called 451 // where any remaining bytes will be filled with NULLs and that the string won't be 452 // NULL terminated if it won't fit in the buffer. 453 454 // If the last character is NULL, then all went well 455 if (path[max_path_length-1] == '\0') 456 return true; 457 458 // Make sure the path is terminated, as it didn't fit into "path" 459 path[max_path_length-1] = '\0'; 460 return false; 461} 462 463//------------------------------------------------------------------ 464// Returns a shared pointer to a data buffer that contains all or 465// part of the contents of a file. The data is memory mapped and 466// will lazily page in data from the file as memory is accessed. 467// The data that is mappped will start "file_offset" bytes into the 468// file, and "file_size" bytes will be mapped. If "file_size" is 469// greater than the number of bytes available in the file starting 470// at "file_offset", the number of bytes will be appropriately 471// truncated. The final number of bytes that get mapped can be 472// verified using the DataBuffer::GetByteSize() function. 473//------------------------------------------------------------------ 474DataBufferSP 475FileSpec::MemoryMapFileContents(off_t file_offset, size_t file_size) const 476{ 477 DataBufferSP data_sp; 478 auto_ptr<DataBufferMemoryMap> mmap_data(new DataBufferMemoryMap()); 479 if (mmap_data.get()) 480 { 481 if (mmap_data->MemoryMapFromFileSpec (this, file_offset, file_size) >= file_size) 482 data_sp.reset(mmap_data.release()); 483 } 484 return data_sp; 485} 486 487 488//------------------------------------------------------------------ 489// Return the size in bytes that this object takes in memory. This 490// returns the size in bytes of this object, not any shared string 491// values it may refer to. 492//------------------------------------------------------------------ 493size_t 494FileSpec::MemorySize() const 495{ 496 return m_filename.MemorySize() + m_directory.MemorySize(); 497} 498 499 500size_t 501FileSpec::ReadFileContents (off_t file_offset, void *dst, size_t dst_len) const 502{ 503 size_t bytes_read = 0; 504 char resolved_path[PATH_MAX]; 505 if (GetPath(resolved_path, sizeof(resolved_path))) 506 { 507 int fd = ::open (resolved_path, O_RDONLY, 0); 508 if (fd != -1) 509 { 510 struct stat file_stats; 511 if (::fstat (fd, &file_stats) == 0) 512 { 513 // Read bytes directly into our basic_string buffer 514 if (file_stats.st_size > 0) 515 { 516 off_t lseek_result = 0; 517 if (file_offset > 0) 518 lseek_result = ::lseek (fd, file_offset, SEEK_SET); 519 520 if (lseek_result == file_offset) 521 { 522 ssize_t n = ::read (fd, dst, dst_len); 523 if (n >= 0) 524 bytes_read = n; 525 } 526 } 527 } 528 } 529 close(fd); 530 } 531 return bytes_read; 532} 533 534//------------------------------------------------------------------ 535// Returns a shared pointer to a data buffer that contains all or 536// part of the contents of a file. The data copies into a heap based 537// buffer that lives in the DataBuffer shared pointer object returned. 538// The data that is cached will start "file_offset" bytes into the 539// file, and "file_size" bytes will be mapped. If "file_size" is 540// greater than the number of bytes available in the file starting 541// at "file_offset", the number of bytes will be appropriately 542// truncated. The final number of bytes that get mapped can be 543// verified using the DataBuffer::GetByteSize() function. 544//------------------------------------------------------------------ 545DataBufferSP 546FileSpec::ReadFileContents (off_t file_offset, size_t file_size) const 547{ 548 DataBufferSP data_sp; 549 char resolved_path[PATH_MAX]; 550 if (GetPath(resolved_path, sizeof(resolved_path))) 551 { 552 int fd = ::open (resolved_path, O_RDONLY, 0); 553 if (fd != -1) 554 { 555 struct stat file_stats; 556 if (::fstat (fd, &file_stats) == 0) 557 { 558 if (file_stats.st_size > 0) 559 { 560 off_t lseek_result = 0; 561 if (file_offset > 0) 562 lseek_result = ::lseek (fd, file_offset, SEEK_SET); 563 564 if (lseek_result < 0) 565 { 566 // Get error from errno 567 } 568 else if (lseek_result == file_offset) 569 { 570 const size_t bytes_left = file_stats.st_size - file_offset; 571 size_t num_bytes_to_read = file_size; 572 if (num_bytes_to_read > bytes_left) 573 num_bytes_to_read = bytes_left; 574 575 std::auto_ptr<DataBufferHeap> data_heap_ap; 576 data_heap_ap.reset(new DataBufferHeap(num_bytes_to_read, '\0')); 577 578 if (data_heap_ap.get()) 579 { 580 ssize_t bytesRead = ::read (fd, (void *)data_heap_ap->GetBytes(), data_heap_ap->GetByteSize()); 581 if (bytesRead >= 0) 582 { 583 // Make sure we read exactly what we asked for and if we got 584 // less, adjust the array 585 if (bytesRead < data_heap_ap->GetByteSize()) 586 data_heap_ap->SetByteSize(bytesRead); 587 data_sp.reset(data_heap_ap.release()); 588 } 589 } 590 } 591 } 592 } 593 } 594 close(fd); 595 } 596 return data_sp; 597} 598 599bool 600FileSpec::ReadFileLines (STLStringArray &lines) 601{ 602 bool ret_val = false; 603 lines.clear(); 604 605 std::string dir_str (m_directory.AsCString()); 606 std::string file_str (m_filename.AsCString()); 607 std::string full_name = dir_str + "/" + file_str; 608 609 ifstream file_stream (full_name.c_str()); 610 611 if (file_stream) 612 { 613 std::string line; 614 while (getline (file_stream, line)) 615 lines.push_back (line); 616 ret_val = true; 617 } 618 619 return ret_val; 620} 621