summaryrefslogtreecommitdiffstats
path: root/lldb/source/Core/FileSpec.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/source/Core/FileSpec.cpp')
-rw-r--r--lldb/source/Core/FileSpec.cpp580
1 files changed, 580 insertions, 0 deletions
diff --git a/lldb/source/Core/FileSpec.cpp b/lldb/source/Core/FileSpec.cpp
new file mode 100644
index 00000000000..305650d8643
--- /dev/null
+++ b/lldb/source/Core/FileSpec.cpp
@@ -0,0 +1,580 @@
+//===-- FileSpec.cpp --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#include <fcntl.h>
+#include <glob.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <fstream>
+
+#include "lldb/Core/FileSpec.h"
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/DataBufferMemoryMap.h"
+#include "lldb/Core/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace std;
+
+static bool
+GetFileStats (const FileSpec *file_spec, struct stat *stats_ptr)
+{
+ char resolved_path[PATH_MAX];
+ if (file_spec->GetPath(&resolved_path[0], sizeof(resolved_path)))
+ return ::stat (resolved_path, stats_ptr) == 0;
+ return false;
+}
+
+static const char*
+GetCachedGlobTildeSlash()
+{
+ static std::string g_tilde;
+ if (g_tilde.empty())
+ {
+ glob_t globbuf;
+ if (::glob("~/", GLOB_TILDE, NULL, &globbuf) == 0) //success
+ {
+ g_tilde = globbuf.gl_pathv[0];
+ ::globfree (&globbuf);
+ }
+ if (g_tilde.empty())
+ return NULL;
+ }
+ return g_tilde.c_str();
+}
+
+int
+FileSpec::Resolve (const char *src_path, char *dst_path, size_t dst_len)
+{
+ if (src_path == NULL || src_path[0] == '\0')
+ return 0;
+
+ // Glob if needed for ~/, otherwise copy in case src_path is same as dst_path...
+ char unglobbed_path[PATH_MAX];
+ if (::strstr (src_path, "~/") == src_path)
+ ::snprintf(unglobbed_path, sizeof(unglobbed_path), "%s%s", GetCachedGlobTildeSlash(), src_path + 2);
+ else
+ ::snprintf(unglobbed_path, sizeof(unglobbed_path), "%s", src_path);
+
+ // Now resolve the path if needed
+ char resolved_path[PATH_MAX];
+ if (::realpath (unglobbed_path, resolved_path))
+ {
+ // Success, copy the resolved path
+ return ::snprintf(dst_path, dst_len, "%s", resolved_path);
+ }
+ else
+ {
+ // Failed, just copy the unglobbed path
+ return ::snprintf(dst_path, dst_len, "%s", unglobbed_path);
+ }
+}
+
+FileSpec::FileSpec() :
+ m_directory(),
+ m_filename()
+{
+}
+
+//------------------------------------------------------------------
+// Default constructor that can take an optional full path to a
+// file on disk.
+//------------------------------------------------------------------
+FileSpec::FileSpec(const char *pathname) :
+ m_directory(),
+ m_filename()
+{
+ if (pathname && pathname[0])
+ SetFile(pathname);
+}
+
+//------------------------------------------------------------------
+// Copy constructor
+//------------------------------------------------------------------
+FileSpec::FileSpec(const FileSpec& rhs) :
+ m_directory (rhs.m_directory),
+ m_filename (rhs.m_filename)
+{
+}
+
+//------------------------------------------------------------------
+// Copy constructor
+//------------------------------------------------------------------
+FileSpec::FileSpec(const FileSpec* rhs) :
+ m_directory(),
+ m_filename()
+{
+ if (rhs)
+ *this = *rhs;
+}
+
+//------------------------------------------------------------------
+// Virtual destrcuctor in case anyone inherits from this class.
+//------------------------------------------------------------------
+FileSpec::~FileSpec()
+{
+}
+
+//------------------------------------------------------------------
+// Assignment operator.
+//------------------------------------------------------------------
+const FileSpec&
+FileSpec::operator= (const FileSpec& rhs)
+{
+ if (this != &rhs)
+ {
+ m_directory = rhs.m_directory;
+ m_filename = rhs.m_filename;
+ }
+ return *this;
+}
+
+
+//------------------------------------------------------------------
+// Update the contents of this object with a new path. The path will
+// be split up into a directory and filename and stored as uniqued
+// string values for quick comparison and efficient memory usage.
+//------------------------------------------------------------------
+void
+FileSpec::SetFile(const char *pathname)
+{
+ m_filename.Clear();
+ m_directory.Clear();
+ if (pathname == NULL || pathname[0] == '\0')
+ return;
+
+ char resolved_path[PATH_MAX];
+
+ if (FileSpec::Resolve (pathname, resolved_path, sizeof(resolved_path)) < sizeof(resolved_path) - 1)
+ {
+ char *filename = ::basename (resolved_path);
+ if (filename)
+ {
+ m_filename.SetCString (filename);
+ // Truncate the basename off the end of the resolved path
+
+ // Only attempt to get the dirname if it looks like we have a path
+ if (strchr(resolved_path, '/'))
+ {
+ char *directory = ::dirname (resolved_path);
+
+ // Make sure we didn't get our directory resolved to "." without having
+ // specified
+ if (directory)
+ m_directory.SetCString(directory);
+ else
+ {
+ char *last_resolved_path_slash = strrchr(resolved_path, '/');
+ if (last_resolved_path_slash)
+ {
+ *last_resolved_path_slash = '\0';
+ m_directory.SetCString(resolved_path);
+ }
+ }
+ }
+ }
+ else
+ m_directory.SetCString(resolved_path);
+ }
+}
+
+//----------------------------------------------------------------------
+// Convert to pointer operator. This allows code to check any FileSpec
+// objects to see if they contain anything valid using code such as:
+//
+// if (file_spec)
+// {}
+//----------------------------------------------------------------------
+FileSpec::operator
+void*() const
+{
+ return (m_directory || m_filename) ? const_cast<FileSpec*>(this) : NULL;
+}
+
+//----------------------------------------------------------------------
+// Logical NOT operator. This allows code to check any FileSpec
+// objects to see if they are invalid using code such as:
+//
+// if (!file_spec)
+// {}
+//----------------------------------------------------------------------
+bool
+FileSpec::operator!() const
+{
+ return !m_directory && !m_filename;
+}
+
+//------------------------------------------------------------------
+// Equal to operator
+//------------------------------------------------------------------
+bool
+FileSpec::operator== (const FileSpec& rhs) const
+{
+ return m_directory == rhs.m_directory && m_filename == rhs.m_filename;
+}
+
+//------------------------------------------------------------------
+// Not equal to operator
+//------------------------------------------------------------------
+bool
+FileSpec::operator!= (const FileSpec& rhs) const
+{
+ return m_filename != rhs.m_filename || m_directory != rhs.m_directory;
+}
+
+//------------------------------------------------------------------
+// Less than operator
+//------------------------------------------------------------------
+bool
+FileSpec::operator< (const FileSpec& rhs) const
+{
+ return FileSpec::Compare(*this, rhs, true) < 0;
+}
+
+//------------------------------------------------------------------
+// Dump a FileSpec object to a stream
+//------------------------------------------------------------------
+Stream&
+lldb_private::operator << (Stream &s, const FileSpec& f)
+{
+ f.Dump(&s);
+ return s;
+}
+
+//------------------------------------------------------------------
+// Clear this object by releasing both the directory and filename
+// string values and making them both the empty string.
+//------------------------------------------------------------------
+void
+FileSpec::Clear()
+{
+ m_directory.Clear();
+ m_filename.Clear();
+}
+
+//------------------------------------------------------------------
+// Compare two FileSpec objects. If "full" is true, then both
+// the directory and the filename must match. If "full" is false,
+// then the directory names for "a" and "b" are only compared if
+// they are both non-empty. This allows a FileSpec object to only
+// contain a filename and it can match FileSpec objects that have
+// matching filenames with different paths.
+//
+// Return -1 if the "a" is less than "b", 0 if "a" is equal to "b"
+// and "1" if "a" is greater than "b".
+//------------------------------------------------------------------
+int
+FileSpec::Compare(const FileSpec& a, const FileSpec& b, bool full)
+{
+ int result = 0;
+
+ // If full is true, then we must compare both the directory and filename.
+
+ // If full is false, then if either directory is empty, then we match on
+ // the basename only, and if both directories have valid values, we still
+ // do a full compare. This allows for matching when we just have a filename
+ // in one of the FileSpec objects.
+
+ if (full || (a.m_directory && b.m_directory))
+ {
+ result = ConstString::Compare(a.m_directory, b.m_directory);
+ if (result)
+ return result;
+ }
+ return ConstString::Compare (a.m_filename, b.m_filename);
+}
+
+bool
+FileSpec::Equal (const FileSpec& a, const FileSpec& b, bool full)
+{
+ if (full)
+ return a == b;
+ else
+ return a.m_filename == b.m_filename;
+}
+
+
+
+//------------------------------------------------------------------
+// Dump the object to the supplied stream. If the object contains
+// a valid directory name, it will be displayed followed by a
+// directory delimiter, and the filename.
+//------------------------------------------------------------------
+void
+FileSpec::Dump(Stream *s) const
+{
+ if (m_filename)
+ m_directory.Dump(s, ""); // Provide a default for m_directory when we dump it in case it is invalid
+
+ if (m_directory)
+ {
+ // If dirname was valid, then we need to print a slash between
+ // the directory and the filename
+ s->PutChar('/');
+ }
+ m_filename.Dump(s);
+}
+
+//------------------------------------------------------------------
+// Returns true if the file exists.
+//------------------------------------------------------------------
+bool
+FileSpec::Exists () const
+{
+ struct stat file_stats;
+ return GetFileStats (this, &file_stats);
+}
+
+uint64_t
+FileSpec::GetByteSize() const
+{
+ struct stat file_stats;
+ if (GetFileStats (this, &file_stats))
+ return file_stats.st_size;
+ return 0;
+}
+
+FileSpec::FileType
+FileSpec::GetFileType () const
+{
+ struct stat file_stats;
+ if (GetFileStats (this, &file_stats))
+ {
+ mode_t file_type = file_stats.st_mode & S_IFMT;
+ switch (file_type)
+ {
+ case S_IFDIR: return eFileTypeDirectory;
+ case S_IFIFO: return eFileTypePipe;
+ case S_IFREG: return eFileTypeRegular;
+ case S_IFSOCK: return eFileTypeSocket;
+ case S_IFLNK: return eFileTypeSymbolicLink;
+ default:
+ break;
+ }
+ return eFileTypeUknown;
+ }
+ return eFileTypeInvalid;
+}
+
+TimeValue
+FileSpec::GetModificationTime () const
+{
+ TimeValue mod_time;
+ struct stat file_stats;
+ if (GetFileStats (this, &file_stats))
+ mod_time = file_stats.st_mtimespec;
+ return mod_time;
+}
+
+//------------------------------------------------------------------
+// Directory string get accessor.
+//------------------------------------------------------------------
+ConstString &
+FileSpec::GetDirectory()
+{
+ return m_directory;
+}
+
+//------------------------------------------------------------------
+// Directory string const get accessor.
+//------------------------------------------------------------------
+const ConstString &
+FileSpec::GetDirectory() const
+{
+ return m_directory;
+}
+
+//------------------------------------------------------------------
+// Filename string get accessor.
+//------------------------------------------------------------------
+ConstString &
+FileSpec::GetFilename()
+{
+ return m_filename;
+}
+
+//------------------------------------------------------------------
+// Filename string const get accessor.
+//------------------------------------------------------------------
+const ConstString &
+FileSpec::GetFilename() const
+{
+ return m_filename;
+}
+
+//------------------------------------------------------------------
+// Extract the directory and path into a fixed buffer. This is
+// needed as the directory and path are stored in separate string
+// values.
+//------------------------------------------------------------------
+bool
+FileSpec::GetPath(char *path, size_t max_path_length) const
+{
+ if (max_path_length == 0)
+ return false;
+
+ path[0] = '\0';
+ const char *dirname = m_directory.AsCString();
+ const char *filename = m_filename.AsCString();
+ if (dirname)
+ {
+ if (filename && filename[0])
+ {
+ return snprintf (path, max_path_length, "%s/%s", dirname, filename) < max_path_length;
+ }
+ else
+ {
+ strncpy (path, dirname, max_path_length);
+ }
+ }
+ else if (filename)
+ {
+ strncpy (path, filename, max_path_length);
+ }
+
+ // Any code paths that reach here assume that strncpy, or a similar function was called
+ // where any remaining bytes will be filled with NULLs and that the string won't be
+ // NULL terminated if it won't fit in the buffer.
+
+ // If the last character is NULL, then all went well
+ if (path[max_path_length-1] == '\0')
+ return true;
+
+ // Make sure the path is terminated, as it didn't fit into "path"
+ path[max_path_length-1] = '\0';
+ return false;
+}
+
+//------------------------------------------------------------------
+// Returns a shared pointer to a data buffer that contains all or
+// part of the contents of a file. The data is memory mapped and
+// will lazily page in data from the file as memory is accessed.
+// The data that is mappped will start "file_offset" bytes into the
+// file, and "file_size" bytes will be mapped. If "file_size" is
+// greater than the number of bytes available in the file starting
+// at "file_offset", the number of bytes will be appropriately
+// truncated. The final number of bytes that get mapped can be
+// verified using the DataBuffer::GetByteSize() function.
+//------------------------------------------------------------------
+DataBufferSP
+FileSpec::MemoryMapFileContents(off_t file_offset, size_t file_size) const
+{
+ DataBufferSP data_sp;
+ auto_ptr<DataBufferMemoryMap> mmap_data(new DataBufferMemoryMap());
+ if (mmap_data.get())
+ {
+ if (mmap_data->MemoryMapFromFileSpec (this, file_offset, file_size) >= file_size)
+ data_sp.reset(mmap_data.release());
+ }
+ return data_sp;
+}
+
+
+//------------------------------------------------------------------
+// Return the size in bytes that this object takes in memory. This
+// returns the size in bytes of this object, not any shared string
+// values it may refer to.
+//------------------------------------------------------------------
+size_t
+FileSpec::MemorySize() const
+{
+ return m_filename.MemorySize() + m_directory.MemorySize();
+}
+
+//------------------------------------------------------------------
+// Returns a shared pointer to a data buffer that contains all or
+// part of the contents of a file. The data copies into a heap based
+// buffer that lives in the DataBuffer shared pointer object returned.
+// The data that is cached will start "file_offset" bytes into the
+// file, and "file_size" bytes will be mapped. If "file_size" is
+// greater than the number of bytes available in the file starting
+// at "file_offset", the number of bytes will be appropriately
+// truncated. The final number of bytes that get mapped can be
+// verified using the DataBuffer::GetByteSize() function.
+//------------------------------------------------------------------
+DataBufferSP
+FileSpec::ReadFileContents(off_t file_offset, size_t file_size) const
+{
+ DataBufferSP data_sp;
+ char resolved_path[PATH_MAX];
+ if (GetPath(resolved_path, sizeof(resolved_path)))
+ {
+ int fd = ::open (resolved_path, O_RDONLY, 0);
+ if (fd != -1)
+ {
+ struct stat file_stats;
+ if (::fstat (fd, &file_stats) == 0)
+ {
+ // Read bytes directly into our basic_string buffer
+ if (file_stats.st_size > 0)
+ {
+ off_t lseek_result = 0;
+ if (file_offset > 0)
+ lseek_result = ::lseek (fd, file_offset, SEEK_SET);
+
+ if (lseek_result < 0)
+ {
+ // Get error from errno
+ }
+ else if (lseek_result == file_offset)
+ {
+ std::auto_ptr<DataBufferHeap> data_heap_ap;
+ if (file_stats.st_size < file_size)
+ data_heap_ap.reset(new DataBufferHeap(file_stats.st_size, '\0'));
+ else
+ data_heap_ap.reset(new DataBufferHeap(file_size, '\0'));
+
+ if (data_heap_ap.get())
+ {
+ ssize_t bytesRead = ::read (fd, (void *)data_heap_ap->GetBytes(), data_heap_ap->GetByteSize());
+ if (bytesRead >= 0)
+ {
+ // Make sure we read exactly what we asked for and if we got
+ // less, adjust the array
+ if (bytesRead < data_heap_ap->GetByteSize())
+ data_heap_ap->SetByteSize(bytesRead);
+ data_sp.reset(data_heap_ap.release());
+ }
+ }
+ }
+ }
+ }
+ }
+ close(fd);
+ }
+ return data_sp;
+}
+
+bool
+FileSpec::ReadFileLines (STLStringArray &lines)
+{
+ bool ret_val = false;
+ lines.clear();
+
+ std::string dir_str (m_directory.AsCString());
+ std::string file_str (m_filename.AsCString());
+ std::string full_name = dir_str + "/" + file_str;
+
+ ifstream file_stream (full_name.c_str());
+
+ if (file_stream)
+ {
+ std::string line;
+ while (getline (file_stream, line))
+ lines.push_back (line);
+ ret_val = true;
+ }
+
+ return ret_val;
+}
OpenPOWER on IntegriCloud