blob: 0f2a6641f9963ff5afcf3881ce11da54f302a3ed [file] [log] [blame]
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001//===-- 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
Virgile Bellob2f1fb22013-08-23 12:44:05 +000010#ifndef _WIN32
Greg Clayton4272cc72011-02-02 02:24:04 +000011#include <dirent.h>
Virgile Bellob2f1fb22013-08-23 12:44:05 +000012#else
13#include "lldb/Host/windows/windows.h"
14#endif
Chris Lattner30fdc8d2010-06-08 16:52:24 +000015#include <fcntl.h>
Virgile Bello69571952013-09-20 22:35:22 +000016#ifndef _MSC_VER
Chris Lattner30fdc8d2010-06-08 16:52:24 +000017#include <libgen.h>
Virgile Bello69571952013-09-20 22:35:22 +000018#endif
Kate Stoneb9c1b512016-09-06 20:57:50 +000019#include <fstream>
Rafael Espindola09079162013-06-13 20:10:23 +000020#include <set>
Greg Claytone0f3c022011-02-07 17:41:11 +000021#include <string.h>
Greg Claytonfd184262011-02-05 02:27:52 +000022
Jim Ingham9035e7c2011-02-07 19:42:39 +000023#include "lldb/Host/Config.h" // Have to include this before we test the define...
Greg Clayton45319462011-02-08 00:35:34 +000024#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER
Jim Inghamf818ca32010-07-01 01:48:53 +000025#include <pwd.h>
Greg Claytonfd184262011-02-05 02:27:52 +000026#endif
Chris Lattner30fdc8d2010-06-08 16:52:24 +000027
Chaoren Linf34f4102015-05-09 01:21:32 +000028#include "lldb/Core/ArchSpec.h"
Zachary Turnerc00cf4a2014-08-15 22:04:21 +000029#include "lldb/Core/DataBufferHeap.h"
30#include "lldb/Core/DataBufferMemoryMap.h"
31#include "lldb/Core/RegularExpression.h"
Zachary Turnerc00cf4a2014-08-15 22:04:21 +000032#include "lldb/Core/Stream.h"
Kate Stoneb9c1b512016-09-06 20:57:50 +000033#include "lldb/Core/StreamString.h"
Zachary Turnerc00cf4a2014-08-15 22:04:21 +000034#include "lldb/Host/File.h"
35#include "lldb/Host/FileSpec.h"
36#include "lldb/Host/FileSystem.h"
37#include "lldb/Host/Host.h"
38#include "lldb/Utility/CleanUp.h"
39
Caroline Tice391a9602010-09-12 00:10:52 +000040#include "llvm/ADT/StringRef.h"
Zachary Turner190fadc2016-03-22 17:58:09 +000041#include "llvm/Support/ConvertUTF.h"
Zachary Turner3f559742014-08-07 17:33:36 +000042#include "llvm/Support/FileSystem.h"
Greg Clayton38a61402010-12-02 23:20:03 +000043#include "llvm/Support/Path.h"
44#include "llvm/Support/Program.h"
Caroline Tice391a9602010-09-12 00:10:52 +000045
Chris Lattner30fdc8d2010-06-08 16:52:24 +000046using namespace lldb;
47using namespace lldb_private;
Chris Lattner30fdc8d2010-06-08 16:52:24 +000048
Chaoren Lin1c614fe2015-05-28 17:02:45 +000049namespace {
50
Kate Stoneb9c1b512016-09-06 20:57:50 +000051bool PathSyntaxIsPosix(FileSpec::PathSyntax syntax) {
52 return (syntax == FileSpec::ePathSyntaxPosix ||
53 (syntax == FileSpec::ePathSyntaxHostNative &&
54 FileSystem::GetNativePathSyntax() == FileSpec::ePathSyntaxPosix));
Chaoren Lin1c614fe2015-05-28 17:02:45 +000055}
56
Kate Stoneb9c1b512016-09-06 20:57:50 +000057const char *GetPathSeparators(FileSpec::PathSyntax syntax) {
58 return PathSyntaxIsPosix(syntax) ? "/" : "\\/";
Pavel Labath144119b2016-04-04 14:39:12 +000059}
60
Kate Stoneb9c1b512016-09-06 20:57:50 +000061char GetPrefferedPathSeparator(FileSpec::PathSyntax syntax) {
62 return GetPathSeparators(syntax)[0];
Pavel Labath144119b2016-04-04 14:39:12 +000063}
64
Kate Stoneb9c1b512016-09-06 20:57:50 +000065bool IsPathSeparator(char value, FileSpec::PathSyntax syntax) {
66 return value == '/' || (!PathSyntaxIsPosix(syntax) && value == '\\');
Chaoren Lin1c614fe2015-05-28 17:02:45 +000067}
68
Kate Stoneb9c1b512016-09-06 20:57:50 +000069void Normalize(llvm::SmallVectorImpl<char> &path, FileSpec::PathSyntax syntax) {
70 if (PathSyntaxIsPosix(syntax))
71 return;
Chaoren Lin1c614fe2015-05-28 17:02:45 +000072
Kate Stoneb9c1b512016-09-06 20:57:50 +000073 std::replace(path.begin(), path.end(), '\\', '/');
74 // Windows path can have \\ slashes which can be changed by replace
75 // call above to //. Here we remove the duplicate.
76 auto iter = std::unique(path.begin(), path.end(), [](char &c1, char &c2) {
77 return (c1 == '/' && c2 == '/');
78 });
79 path.erase(iter, path.end());
Chaoren Lin1c614fe2015-05-28 17:02:45 +000080}
81
Kate Stoneb9c1b512016-09-06 20:57:50 +000082void Denormalize(llvm::SmallVectorImpl<char> &path,
83 FileSpec::PathSyntax syntax) {
84 if (PathSyntaxIsPosix(syntax))
85 return;
Chaoren Lin1c614fe2015-05-28 17:02:45 +000086
Kate Stoneb9c1b512016-09-06 20:57:50 +000087 std::replace(path.begin(), path.end(), '/', '\\');
Chaoren Lin1c614fe2015-05-28 17:02:45 +000088}
89
Kate Stoneb9c1b512016-09-06 20:57:50 +000090bool GetFileStats(const FileSpec *file_spec, struct stat *stats_ptr) {
91 char resolved_path[PATH_MAX];
92 if (file_spec->GetPath(resolved_path, sizeof(resolved_path)))
93 return FileSystem::Stat(resolved_path, stats_ptr) == 0;
94 return false;
Chris Lattner30fdc8d2010-06-08 16:52:24 +000095}
96
Kate Stoneb9c1b512016-09-06 20:57:50 +000097size_t FilenamePos(llvm::StringRef str, FileSpec::PathSyntax syntax) {
98 if (str.size() == 2 && IsPathSeparator(str[0], syntax) && str[0] == str[1])
99 return 0;
Pavel Labath144119b2016-04-04 14:39:12 +0000100
Kate Stoneb9c1b512016-09-06 20:57:50 +0000101 if (str.size() > 0 && IsPathSeparator(str.back(), syntax))
102 return str.size() - 1;
Pavel Labath144119b2016-04-04 14:39:12 +0000103
Kate Stoneb9c1b512016-09-06 20:57:50 +0000104 size_t pos = str.find_last_of(GetPathSeparators(syntax), str.size() - 1);
Pavel Labath144119b2016-04-04 14:39:12 +0000105
Kate Stoneb9c1b512016-09-06 20:57:50 +0000106 if (!PathSyntaxIsPosix(syntax) && pos == llvm::StringRef::npos)
107 pos = str.find_last_of(':', str.size() - 2);
Pavel Labath144119b2016-04-04 14:39:12 +0000108
Kate Stoneb9c1b512016-09-06 20:57:50 +0000109 if (pos == llvm::StringRef::npos ||
110 (pos == 1 && IsPathSeparator(str[0], syntax)))
111 return 0;
Pavel Labath144119b2016-04-04 14:39:12 +0000112
Kate Stoneb9c1b512016-09-06 20:57:50 +0000113 return pos + 1;
Chaoren Lin1c614fe2015-05-28 17:02:45 +0000114}
115
Kate Stoneb9c1b512016-09-06 20:57:50 +0000116size_t RootDirStart(llvm::StringRef str, FileSpec::PathSyntax syntax) {
117 // case "c:/"
118 if (!PathSyntaxIsPosix(syntax) &&
119 (str.size() > 2 && str[1] == ':' && IsPathSeparator(str[2], syntax)))
120 return 2;
Pavel Labath144119b2016-04-04 14:39:12 +0000121
Kate Stoneb9c1b512016-09-06 20:57:50 +0000122 // case "//"
123 if (str.size() == 2 && IsPathSeparator(str[0], syntax) && str[0] == str[1])
Pavel Labath144119b2016-04-04 14:39:12 +0000124 return llvm::StringRef::npos;
Kate Stoneb9c1b512016-09-06 20:57:50 +0000125
126 // case "//net"
127 if (str.size() > 3 && IsPathSeparator(str[0], syntax) && str[0] == str[1] &&
128 !IsPathSeparator(str[2], syntax))
129 return str.find_first_of(GetPathSeparators(syntax), 2);
130
131 // case "/"
132 if (str.size() > 0 && IsPathSeparator(str[0], syntax))
133 return 0;
134
135 return llvm::StringRef::npos;
Pavel Labath144119b2016-04-04 14:39:12 +0000136}
137
Kate Stoneb9c1b512016-09-06 20:57:50 +0000138size_t ParentPathEnd(llvm::StringRef path, FileSpec::PathSyntax syntax) {
139 size_t end_pos = FilenamePos(path, syntax);
Pavel Labath144119b2016-04-04 14:39:12 +0000140
Kate Stoneb9c1b512016-09-06 20:57:50 +0000141 bool filename_was_sep =
142 path.size() > 0 && IsPathSeparator(path[end_pos], syntax);
Pavel Labath144119b2016-04-04 14:39:12 +0000143
Kate Stoneb9c1b512016-09-06 20:57:50 +0000144 // Skip separators except for root dir.
145 size_t root_dir_pos = RootDirStart(path.substr(0, end_pos), syntax);
Pavel Labath144119b2016-04-04 14:39:12 +0000146
Kate Stoneb9c1b512016-09-06 20:57:50 +0000147 while (end_pos > 0 && (end_pos - 1) != root_dir_pos &&
148 IsPathSeparator(path[end_pos - 1], syntax))
149 --end_pos;
Pavel Labath144119b2016-04-04 14:39:12 +0000150
Kate Stoneb9c1b512016-09-06 20:57:50 +0000151 if (end_pos == 1 && root_dir_pos == 0 && filename_was_sep)
152 return llvm::StringRef::npos;
Pavel Labath144119b2016-04-04 14:39:12 +0000153
Kate Stoneb9c1b512016-09-06 20:57:50 +0000154 return end_pos;
Pavel Labath144119b2016-04-04 14:39:12 +0000155}
156
157} // end anonymous namespace
158
Jim Inghamf818ca32010-07-01 01:48:53 +0000159// Resolves the username part of a path of the form ~user/other/directories, and
Kate Stoneb9c1b512016-09-06 20:57:50 +0000160// writes the result into dst_path. This will also resolve "~" to the current
161// user.
162// If you want to complete "~" to the list of users, pass it to
163// ResolvePartialUsername.
164void FileSpec::ResolveUsername(llvm::SmallVectorImpl<char> &path) {
Zachary Turner3f559742014-08-07 17:33:36 +0000165#if LLDB_CONFIG_TILDE_RESOLVES_TO_USER
Kate Stoneb9c1b512016-09-06 20:57:50 +0000166 if (path.empty() || path[0] != '~')
167 return;
Zachary Turner3f559742014-08-07 17:33:36 +0000168
Kate Stoneb9c1b512016-09-06 20:57:50 +0000169 llvm::StringRef path_str(path.data(), path.size());
170 size_t slash_pos = path_str.find('/', 1);
171 if (slash_pos == 1 || path.size() == 1) {
172 // A path of ~/ resolves to the current user's home dir
173 llvm::SmallString<64> home_dir;
174 // llvm::sys::path::home_directory() only checks if "HOME" is set in the
175 // environment and does nothing else to locate the user home directory
176 if (!llvm::sys::path::home_directory(home_dir)) {
177 struct passwd *pw = getpwuid(getuid());
178 if (pw && pw->pw_dir && pw->pw_dir[0]) {
179 // Update our environemnt so llvm::sys::path::home_directory() works
180 // next time
181 setenv("HOME", pw->pw_dir, 0);
182 home_dir.assign(llvm::StringRef(pw->pw_dir));
183 } else {
184 return;
185 }
Jim Inghamf818ca32010-07-01 01:48:53 +0000186 }
Kate Stoneb9c1b512016-09-06 20:57:50 +0000187
188 // Overwrite the ~ with the first character of the homedir, and insert
189 // the rest. This way we only trigger one move, whereas an insert
190 // followed by a delete (or vice versa) would trigger two.
191 path[0] = home_dir[0];
192 path.insert(path.begin() + 1, home_dir.begin() + 1, home_dir.end());
193 return;
194 }
195
196 auto username_begin = path.begin() + 1;
197 auto username_end = (slash_pos == llvm::StringRef::npos)
198 ? path.end()
199 : (path.begin() + slash_pos);
200 size_t replacement_length = std::distance(path.begin(), username_end);
201
202 llvm::SmallString<20> username(username_begin, username_end);
203 struct passwd *user_entry = ::getpwnam(username.c_str());
204 if (user_entry != nullptr) {
205 // Copy over the first n characters of the path, where n is the smaller of
206 // the length
207 // of the home directory and the slash pos.
208 llvm::StringRef homedir(user_entry->pw_dir);
209 size_t initial_copy_length = std::min(homedir.size(), replacement_length);
210 auto src_begin = homedir.begin();
211 auto src_end = src_begin + initial_copy_length;
212 std::copy(src_begin, src_end, path.begin());
213 if (replacement_length > homedir.size()) {
214 // We copied the entire home directory, but the ~username portion of the
215 // path was
216 // longer, so there's characters that need to be removed.
217 path.erase(path.begin() + initial_copy_length, username_end);
218 } else if (replacement_length < homedir.size()) {
219 // We copied all the way up to the slash in the destination, but there's
220 // still more
221 // characters that need to be inserted.
222 path.insert(username_end, src_end, homedir.end());
Jim Inghamf818ca32010-07-01 01:48:53 +0000223 }
Kate Stoneb9c1b512016-09-06 20:57:50 +0000224 } else {
225 // Unable to resolve username (user doesn't exist?)
226 path.clear();
227 }
Zachary Turner3f559742014-08-07 17:33:36 +0000228#endif
Jim Inghamf818ca32010-07-01 01:48:53 +0000229}
230
Kate Stoneb9c1b512016-09-06 20:57:50 +0000231size_t FileSpec::ResolvePartialUsername(const char *partial_name,
232 StringList &matches) {
Jim Ingham84363072011-02-08 23:24:09 +0000233#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER
Kate Stoneb9c1b512016-09-06 20:57:50 +0000234 size_t extant_entries = matches.GetSize();
235
236 setpwent();
237 struct passwd *user_entry;
238 const char *name_start = partial_name + 1;
239 std::set<std::string> name_list;
240
241 while ((user_entry = getpwent()) != NULL) {
242 if (strstr(user_entry->pw_name, name_start) == user_entry->pw_name) {
243 std::string tmp_buf("~");
244 tmp_buf.append(user_entry->pw_name);
245 tmp_buf.push_back('/');
246 name_list.insert(tmp_buf);
Jim Ingham84363072011-02-08 23:24:09 +0000247 }
Kate Stoneb9c1b512016-09-06 20:57:50 +0000248 }
249 std::set<std::string>::iterator pos, end = name_list.end();
250 for (pos = name_list.begin(); pos != end; pos++) {
251 matches.AppendString((*pos).c_str());
252 }
253 return matches.GetSize() - extant_entries;
Jim Ingham84363072011-02-08 23:24:09 +0000254#else
Kate Stoneb9c1b512016-09-06 20:57:50 +0000255 // Resolving home directories is not supported, just copy the path...
256 return 0;
257#endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER
Jim Ingham84363072011-02-08 23:24:09 +0000258}
259
Kate Stoneb9c1b512016-09-06 20:57:50 +0000260void FileSpec::Resolve(llvm::SmallVectorImpl<char> &path) {
261 if (path.empty())
262 return;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000263
Greg Clayton45319462011-02-08 00:35:34 +0000264#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER
Kate Stoneb9c1b512016-09-06 20:57:50 +0000265 if (path[0] == '~')
266 ResolveUsername(path);
Greg Clayton45319462011-02-08 00:35:34 +0000267#endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000268
Kate Stoneb9c1b512016-09-06 20:57:50 +0000269 // Save a copy of the original path that's passed in
270 llvm::SmallString<128> original_path(path.begin(), path.end());
Jason Molenda671a29d2015-02-25 02:35:25 +0000271
Kate Stoneb9c1b512016-09-06 20:57:50 +0000272 llvm::sys::fs::make_absolute(path);
273 if (!llvm::sys::fs::exists(path)) {
274 path.clear();
275 path.append(original_path.begin(), original_path.end());
276 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000277}
278
Kate Stoneb9c1b512016-09-06 20:57:50 +0000279FileSpec::FileSpec()
280 : m_directory(), m_filename(), m_syntax(FileSystem::GetNativePathSyntax()) {
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000281}
282
283//------------------------------------------------------------------
284// Default constructor that can take an optional full path to a
285// file on disk.
286//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000287FileSpec::FileSpec(const char *pathname, bool resolve_path, PathSyntax syntax)
288 : m_directory(), m_filename(), m_is_resolved(false), m_syntax(syntax) {
289 if (pathname && pathname[0])
290 SetFile(pathname, resolve_path, syntax);
Jim Ingham0909e5f2010-09-16 00:57:33 +0000291}
292
Kate Stoneb9c1b512016-09-06 20:57:50 +0000293FileSpec::FileSpec(const char *pathname, bool resolve_path, ArchSpec arch)
294 : FileSpec{pathname, resolve_path, arch.GetTriple().isOSWindows()
295 ? ePathSyntaxWindows
296 : ePathSyntaxPosix} {}
Chaoren Lind3173f32015-05-29 19:52:29 +0000297
Kate Stoneb9c1b512016-09-06 20:57:50 +0000298FileSpec::FileSpec(const std::string &path, bool resolve_path,
299 PathSyntax syntax)
300 : FileSpec{path.c_str(), resolve_path, syntax} {}
Chaoren Lind3173f32015-05-29 19:52:29 +0000301
Kate Stoneb9c1b512016-09-06 20:57:50 +0000302FileSpec::FileSpec(const std::string &path, bool resolve_path, ArchSpec arch)
303 : FileSpec{path.c_str(), resolve_path, arch} {}
Chaoren Linf34f4102015-05-09 01:21:32 +0000304
Jim Ingham0909e5f2010-09-16 00:57:33 +0000305//------------------------------------------------------------------
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000306// Copy constructor
307//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000308FileSpec::FileSpec(const FileSpec &rhs)
309 : m_directory(rhs.m_directory), m_filename(rhs.m_filename),
310 m_is_resolved(rhs.m_is_resolved), m_syntax(rhs.m_syntax) {}
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000311
312//------------------------------------------------------------------
313// Copy constructor
314//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000315FileSpec::FileSpec(const FileSpec *rhs) : m_directory(), m_filename() {
316 if (rhs)
317 *this = *rhs;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000318}
319
320//------------------------------------------------------------------
Bruce Mitchenerd93c4a32014-07-01 21:22:11 +0000321// Virtual destructor in case anyone inherits from this class.
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000322//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000323FileSpec::~FileSpec() {}
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000324
325//------------------------------------------------------------------
326// Assignment operator.
327//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000328const FileSpec &FileSpec::operator=(const FileSpec &rhs) {
329 if (this != &rhs) {
330 m_directory = rhs.m_directory;
331 m_filename = rhs.m_filename;
332 m_is_resolved = rhs.m_is_resolved;
333 m_syntax = rhs.m_syntax;
334 }
335 return *this;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000336}
337
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000338//------------------------------------------------------------------
339// Update the contents of this object with a new path. The path will
340// be split up into a directory and filename and stored as uniqued
341// string values for quick comparison and efficient memory usage.
342//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000343void FileSpec::SetFile(const char *pathname, bool resolve, PathSyntax syntax) {
344 m_filename.Clear();
345 m_directory.Clear();
346 m_is_resolved = false;
347 m_syntax = (syntax == ePathSyntaxHostNative)
348 ? FileSystem::GetNativePathSyntax()
349 : syntax;
Zachary Turnerdf62f202014-08-07 17:33:07 +0000350
Kate Stoneb9c1b512016-09-06 20:57:50 +0000351 if (pathname == NULL || pathname[0] == '\0')
352 return;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000353
Kate Stoneb9c1b512016-09-06 20:57:50 +0000354 llvm::SmallString<64> resolved(pathname);
Zachary Turnerdf62f202014-08-07 17:33:07 +0000355
Kate Stoneb9c1b512016-09-06 20:57:50 +0000356 if (resolve) {
357 FileSpec::Resolve(resolved);
358 m_is_resolved = true;
359 }
Zachary Turnerc7a17ed2014-12-01 23:13:32 +0000360
Kate Stoneb9c1b512016-09-06 20:57:50 +0000361 Normalize(resolved, syntax);
Pavel Labatha212c582016-04-14 09:38:06 +0000362
Kate Stoneb9c1b512016-09-06 20:57:50 +0000363 llvm::StringRef resolve_path_ref(resolved.c_str());
364 size_t dir_end = ParentPathEnd(resolve_path_ref, syntax);
365 if (dir_end == 0) {
366 m_filename.SetString(resolve_path_ref);
367 return;
368 }
Pavel Labath144119b2016-04-04 14:39:12 +0000369
Kate Stoneb9c1b512016-09-06 20:57:50 +0000370 m_directory.SetString(resolve_path_ref.substr(0, dir_end));
Pavel Labath144119b2016-04-04 14:39:12 +0000371
Kate Stoneb9c1b512016-09-06 20:57:50 +0000372 size_t filename_begin = dir_end;
373 size_t root_dir_start = RootDirStart(resolve_path_ref, syntax);
374 while (filename_begin != llvm::StringRef::npos &&
375 filename_begin < resolve_path_ref.size() &&
376 filename_begin != root_dir_start &&
377 IsPathSeparator(resolve_path_ref[filename_begin], syntax))
378 ++filename_begin;
379 m_filename.SetString((filename_begin == llvm::StringRef::npos ||
380 filename_begin >= resolve_path_ref.size())
381 ? "."
382 : resolve_path_ref.substr(filename_begin));
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000383}
384
Kate Stoneb9c1b512016-09-06 20:57:50 +0000385void FileSpec::SetFile(const char *pathname, bool resolve, ArchSpec arch) {
386 return SetFile(pathname, resolve, arch.GetTriple().isOSWindows()
387 ? ePathSyntaxWindows
388 : ePathSyntaxPosix);
Chaoren Lin44145d72015-05-29 19:52:37 +0000389}
390
Kate Stoneb9c1b512016-09-06 20:57:50 +0000391void FileSpec::SetFile(const std::string &pathname, bool resolve,
392 PathSyntax syntax) {
393 return SetFile(pathname.c_str(), resolve, syntax);
Chaoren Lind3173f32015-05-29 19:52:29 +0000394}
395
Kate Stoneb9c1b512016-09-06 20:57:50 +0000396void FileSpec::SetFile(const std::string &pathname, bool resolve,
397 ArchSpec arch) {
398 return SetFile(pathname.c_str(), resolve, arch);
Chaoren Lin44145d72015-05-29 19:52:37 +0000399}
400
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000401//----------------------------------------------------------------------
402// Convert to pointer operator. This allows code to check any FileSpec
403// objects to see if they contain anything valid using code such as:
404//
405// if (file_spec)
406// {}
407//----------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000408FileSpec::operator bool() const { return m_filename || m_directory; }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000409
410//----------------------------------------------------------------------
411// Logical NOT operator. This allows code to check any FileSpec
412// objects to see if they are invalid using code such as:
413//
414// if (!file_spec)
415// {}
416//----------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000417bool FileSpec::operator!() const { return !m_directory && !m_filename; }
418
419bool FileSpec::DirectoryEquals(const FileSpec &rhs) const {
420 const bool case_sensitive = IsCaseSensitive() || rhs.IsCaseSensitive();
421 return ConstString::Equals(m_directory, rhs.m_directory, case_sensitive);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000422}
423
Kate Stoneb9c1b512016-09-06 20:57:50 +0000424bool FileSpec::FileEquals(const FileSpec &rhs) const {
425 const bool case_sensitive = IsCaseSensitive() || rhs.IsCaseSensitive();
426 return ConstString::Equals(m_filename, rhs.m_filename, case_sensitive);
Zachary Turner74e08ca2016-03-02 22:05:52 +0000427}
428
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000429//------------------------------------------------------------------
430// Equal to operator
431//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000432bool FileSpec::operator==(const FileSpec &rhs) const {
433 if (!FileEquals(rhs))
434 return false;
435 if (DirectoryEquals(rhs))
436 return true;
Zachary Turner47c03462016-02-24 21:26:47 +0000437
Kate Stoneb9c1b512016-09-06 20:57:50 +0000438 // TODO: determine if we want to keep this code in here.
439 // The code below was added to handle a case where we were
440 // trying to set a file and line breakpoint and one path
441 // was resolved, and the other not and the directory was
442 // in a mount point that resolved to a more complete path:
443 // "/tmp/a.c" == "/private/tmp/a.c". I might end up pulling
444 // this out...
445 if (IsResolved() && rhs.IsResolved()) {
446 // Both paths are resolved, no need to look further...
447 return false;
448 }
Zachary Turner47c03462016-02-24 21:26:47 +0000449
Kate Stoneb9c1b512016-09-06 20:57:50 +0000450 FileSpec resolved_lhs(*this);
Zachary Turner47c03462016-02-24 21:26:47 +0000451
Kate Stoneb9c1b512016-09-06 20:57:50 +0000452 // If "this" isn't resolved, resolve it
453 if (!IsResolved()) {
454 if (resolved_lhs.ResolvePath()) {
455 // This path wasn't resolved but now it is. Check if the resolved
456 // directory is the same as our unresolved directory, and if so,
457 // we can mark this object as resolved to avoid more future resolves
458 m_is_resolved = (m_directory == resolved_lhs.m_directory);
459 } else
460 return false;
461 }
Zachary Turner47c03462016-02-24 21:26:47 +0000462
Kate Stoneb9c1b512016-09-06 20:57:50 +0000463 FileSpec resolved_rhs(rhs);
464 if (!rhs.IsResolved()) {
465 if (resolved_rhs.ResolvePath()) {
466 // rhs's path wasn't resolved but now it is. Check if the resolved
467 // directory is the same as rhs's unresolved directory, and if so,
468 // we can mark this object as resolved to avoid more future resolves
469 rhs.m_is_resolved = (rhs.m_directory == resolved_rhs.m_directory);
470 } else
471 return false;
472 }
Zachary Turner47c03462016-02-24 21:26:47 +0000473
Kate Stoneb9c1b512016-09-06 20:57:50 +0000474 // If we reach this point in the code we were able to resolve both paths
475 // and since we only resolve the paths if the basenames are equal, then
476 // we can just check if both directories are equal...
477 return DirectoryEquals(rhs);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000478}
479
480//------------------------------------------------------------------
481// Not equal to operator
482//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000483bool FileSpec::operator!=(const FileSpec &rhs) const { return !(*this == rhs); }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000484
485//------------------------------------------------------------------
486// Less than operator
487//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000488bool FileSpec::operator<(const FileSpec &rhs) const {
489 return FileSpec::Compare(*this, rhs, true) < 0;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000490}
491
492//------------------------------------------------------------------
493// Dump a FileSpec object to a stream
494//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000495Stream &lldb_private::operator<<(Stream &s, const FileSpec &f) {
496 f.Dump(&s);
497 return s;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000498}
499
500//------------------------------------------------------------------
501// Clear this object by releasing both the directory and filename
502// string values and making them both the empty string.
503//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000504void FileSpec::Clear() {
505 m_directory.Clear();
506 m_filename.Clear();
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000507}
508
509//------------------------------------------------------------------
510// Compare two FileSpec objects. If "full" is true, then both
511// the directory and the filename must match. If "full" is false,
512// then the directory names for "a" and "b" are only compared if
513// they are both non-empty. This allows a FileSpec object to only
514// contain a filename and it can match FileSpec objects that have
515// matching filenames with different paths.
516//
517// Return -1 if the "a" is less than "b", 0 if "a" is equal to "b"
518// and "1" if "a" is greater than "b".
519//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000520int FileSpec::Compare(const FileSpec &a, const FileSpec &b, bool full) {
521 int result = 0;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000522
Kate Stoneb9c1b512016-09-06 20:57:50 +0000523 // case sensitivity of compare
524 const bool case_sensitive = a.IsCaseSensitive() || b.IsCaseSensitive();
Zachary Turner47c03462016-02-24 21:26:47 +0000525
Kate Stoneb9c1b512016-09-06 20:57:50 +0000526 // If full is true, then we must compare both the directory and filename.
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000527
Kate Stoneb9c1b512016-09-06 20:57:50 +0000528 // If full is false, then if either directory is empty, then we match on
529 // the basename only, and if both directories have valid values, we still
530 // do a full compare. This allows for matching when we just have a filename
531 // in one of the FileSpec objects.
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000532
Kate Stoneb9c1b512016-09-06 20:57:50 +0000533 if (full || (a.m_directory && b.m_directory)) {
534 result = ConstString::Compare(a.m_directory, b.m_directory, case_sensitive);
535 if (result)
536 return result;
537 }
538 return ConstString::Compare(a.m_filename, b.m_filename, case_sensitive);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000539}
540
Kate Stoneb9c1b512016-09-06 20:57:50 +0000541bool FileSpec::Equal(const FileSpec &a, const FileSpec &b, bool full,
542 bool remove_backups) {
543 // case sensitivity of equality test
544 const bool case_sensitive = a.IsCaseSensitive() || b.IsCaseSensitive();
Zachary Turner47c03462016-02-24 21:26:47 +0000545
Kate Stoneb9c1b512016-09-06 20:57:50 +0000546 if (!full && (a.GetDirectory().IsEmpty() || b.GetDirectory().IsEmpty()))
547 return ConstString::Equals(a.m_filename, b.m_filename, case_sensitive);
548 else if (remove_backups == false)
549 return a == b;
550 else {
551 if (!ConstString::Equals(a.m_filename, b.m_filename, case_sensitive))
552 return false;
553 if (ConstString::Equals(a.m_directory, b.m_directory, case_sensitive))
554 return true;
555 ConstString a_without_dots;
556 ConstString b_without_dots;
Jim Ingham96a15962014-11-15 01:54:26 +0000557
Kate Stoneb9c1b512016-09-06 20:57:50 +0000558 RemoveBackupDots(a.m_directory, a_without_dots);
559 RemoveBackupDots(b.m_directory, b_without_dots);
560 return ConstString::Equals(a_without_dots, b_without_dots, case_sensitive);
561 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000562}
563
Kate Stoneb9c1b512016-09-06 20:57:50 +0000564void FileSpec::NormalizePath() {
565 ConstString normalized_directory;
566 FileSpec::RemoveBackupDots(m_directory, normalized_directory);
567 m_directory = normalized_directory;
Greg Clayton5a271952015-06-02 22:43:29 +0000568}
569
Kate Stoneb9c1b512016-09-06 20:57:50 +0000570void FileSpec::RemoveBackupDots(const ConstString &input_const_str,
571 ConstString &result_const_str) {
572 const char *input = input_const_str.GetCString();
573 result_const_str.Clear();
574 if (!input || input[0] == '\0')
Jim Ingham96a15962014-11-15 01:54:26 +0000575 return;
Kate Stoneb9c1b512016-09-06 20:57:50 +0000576
577 const char win_sep = '\\';
578 const char unix_sep = '/';
579 char found_sep;
580 const char *win_backup = "\\..";
581 const char *unix_backup = "/..";
582
583 bool is_win = false;
584
585 // Determine the platform for the path (win or unix):
586
587 if (input[0] == win_sep)
588 is_win = true;
589 else if (input[0] == unix_sep)
590 is_win = false;
591 else if (input[1] == ':')
592 is_win = true;
593 else if (strchr(input, unix_sep) != nullptr)
594 is_win = false;
595 else if (strchr(input, win_sep) != nullptr)
596 is_win = true;
597 else {
598 // No separators at all, no reason to do any work here.
599 result_const_str = input_const_str;
600 return;
601 }
602
603 llvm::StringRef backup_sep;
604 if (is_win) {
605 found_sep = win_sep;
606 backup_sep = win_backup;
607 } else {
608 found_sep = unix_sep;
609 backup_sep = unix_backup;
610 }
611
612 llvm::StringRef input_ref(input);
613 llvm::StringRef curpos(input);
614
615 bool had_dots = false;
616 std::string result;
617
618 while (1) {
619 // Start of loop
620 llvm::StringRef before_sep;
621 std::pair<llvm::StringRef, llvm::StringRef> around_sep =
622 curpos.split(backup_sep);
623
624 before_sep = around_sep.first;
625 curpos = around_sep.second;
626
627 if (curpos.empty()) {
628 if (had_dots) {
629 while (before_sep.startswith("//"))
630 before_sep = before_sep.substr(1);
631 if (!before_sep.empty()) {
632 result.append(before_sep.data(), before_sep.size());
633 }
634 }
635 break;
636 }
637 had_dots = true;
638
639 unsigned num_backups = 1;
640 while (curpos.startswith(backup_sep)) {
641 num_backups++;
642 curpos = curpos.slice(backup_sep.size(), curpos.size());
643 }
644
645 size_t end_pos = before_sep.size();
646 while (num_backups-- > 0) {
647 end_pos = before_sep.rfind(found_sep, end_pos);
648 if (end_pos == llvm::StringRef::npos) {
649 result_const_str = input_const_str;
650 return;
651 }
652 }
653 result.append(before_sep.data(), end_pos);
654 }
655
656 if (had_dots)
657 result_const_str.SetCString(result.c_str());
658 else
659 result_const_str = input_const_str;
660
661 return;
Jim Ingham96a15962014-11-15 01:54:26 +0000662}
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000663
664//------------------------------------------------------------------
665// Dump the object to the supplied stream. If the object contains
666// a valid directory name, it will be displayed followed by a
667// directory delimiter, and the filename.
668//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000669void FileSpec::Dump(Stream *s) const {
670 if (s) {
671 std::string path{GetPath(true)};
672 s->PutCString(path.c_str());
673 char path_separator = GetPrefferedPathSeparator(m_syntax);
674 if (!m_filename && !path.empty() && path.back() != path_separator)
675 s->PutChar(path_separator);
676 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000677}
678
679//------------------------------------------------------------------
680// Returns true if the file exists.
681//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000682bool FileSpec::Exists() const {
683 struct stat file_stats;
684 return GetFileStats(this, &file_stats);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000685}
686
Kate Stoneb9c1b512016-09-06 20:57:50 +0000687bool FileSpec::Readable() const {
688 const uint32_t permissions = GetPermissions();
689 if (permissions & eFilePermissionsEveryoneR)
690 return true;
691 return false;
Greg Clayton5acc1252014-08-15 18:00:45 +0000692}
693
Kate Stoneb9c1b512016-09-06 20:57:50 +0000694bool FileSpec::ResolveExecutableLocation() {
695 if (!m_directory) {
696 const char *file_cstr = m_filename.GetCString();
697 if (file_cstr) {
698 const std::string file_str(file_cstr);
699 llvm::ErrorOr<std::string> error_or_path =
700 llvm::sys::findProgramByName(file_str);
701 if (!error_or_path)
Jim Ingham0909e5f2010-09-16 00:57:33 +0000702 return false;
Kate Stoneb9c1b512016-09-06 20:57:50 +0000703 std::string path = error_or_path.get();
704 llvm::StringRef dir_ref = llvm::sys::path::parent_path(path);
705 if (!dir_ref.empty()) {
706 // FindProgramByName returns "." if it can't find the file.
707 if (strcmp(".", dir_ref.data()) == 0)
708 return false;
709
710 m_directory.SetCString(dir_ref.data());
711 if (Exists())
712 return true;
713 else {
714 // If FindProgramByName found the file, it returns the directory +
715 // filename in its return results.
716 // We need to separate them.
717 FileSpec tmp_file(dir_ref.data(), false);
718 if (tmp_file.Exists()) {
719 m_directory = tmp_file.m_directory;
720 return true;
721 }
722 }
723 }
724 }
725 }
726
727 return false;
Jim Ingham0909e5f2010-09-16 00:57:33 +0000728}
729
Kate Stoneb9c1b512016-09-06 20:57:50 +0000730bool FileSpec::ResolvePath() {
731 if (m_is_resolved)
732 return true; // We have already resolved this path
733
734 char path_buf[PATH_MAX];
735 if (!GetPath(path_buf, PATH_MAX, false))
736 return false;
737 // SetFile(...) will set m_is_resolved correctly if it can resolve the path
738 SetFile(path_buf, true);
739 return m_is_resolved;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000740}
741
Kate Stoneb9c1b512016-09-06 20:57:50 +0000742uint64_t FileSpec::GetByteSize() const {
743 struct stat file_stats;
744 if (GetFileStats(this, &file_stats))
745 return file_stats.st_size;
746 return 0;
Zachary Turnerdf62f202014-08-07 17:33:07 +0000747}
748
Kate Stoneb9c1b512016-09-06 20:57:50 +0000749FileSpec::PathSyntax FileSpec::GetPathSyntax() const { return m_syntax; }
750
751FileSpec::FileType FileSpec::GetFileType() const {
752 struct stat file_stats;
753 if (GetFileStats(this, &file_stats)) {
754 mode_t file_type = file_stats.st_mode & S_IFMT;
755 switch (file_type) {
756 case S_IFDIR:
757 return eFileTypeDirectory;
758 case S_IFREG:
759 return eFileTypeRegular;
Virgile Bellob2f1fb22013-08-23 12:44:05 +0000760#ifndef _WIN32
Kate Stoneb9c1b512016-09-06 20:57:50 +0000761 case S_IFIFO:
762 return eFileTypePipe;
763 case S_IFSOCK:
764 return eFileTypeSocket;
765 case S_IFLNK:
766 return eFileTypeSymbolicLink;
Virgile Bellob2f1fb22013-08-23 12:44:05 +0000767#endif
Kate Stoneb9c1b512016-09-06 20:57:50 +0000768 default:
769 break;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000770 }
Kate Stoneb9c1b512016-09-06 20:57:50 +0000771 return eFileTypeUnknown;
772 }
773 return eFileTypeInvalid;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000774}
775
Kate Stoneb9c1b512016-09-06 20:57:50 +0000776bool FileSpec::IsSymbolicLink() const {
777 char resolved_path[PATH_MAX];
778 if (!GetPath(resolved_path, sizeof(resolved_path)))
779 return false;
Oleksiy Vyalov8a578bf2015-07-21 01:28:22 +0000780
781#ifdef _WIN32
Kate Stoneb9c1b512016-09-06 20:57:50 +0000782 std::wstring wpath;
783 if (!llvm::ConvertUTF8toWide(resolved_path, wpath))
784 return false;
785 auto attrs = ::GetFileAttributesW(wpath.c_str());
786 if (attrs == INVALID_FILE_ATTRIBUTES)
787 return false;
Oleksiy Vyalov8a578bf2015-07-21 01:28:22 +0000788
Kate Stoneb9c1b512016-09-06 20:57:50 +0000789 return (attrs & FILE_ATTRIBUTE_REPARSE_POINT);
Oleksiy Vyalov8a578bf2015-07-21 01:28:22 +0000790#else
Kate Stoneb9c1b512016-09-06 20:57:50 +0000791 struct stat file_stats;
792 if (::lstat(resolved_path, &file_stats) != 0)
793 return false;
Oleksiy Vyalov8a578bf2015-07-21 01:28:22 +0000794
Kate Stoneb9c1b512016-09-06 20:57:50 +0000795 return (file_stats.st_mode & S_IFMT) == S_IFLNK;
Oleksiy Vyalov8a578bf2015-07-21 01:28:22 +0000796#endif
797}
798
Kate Stoneb9c1b512016-09-06 20:57:50 +0000799uint32_t FileSpec::GetPermissions() const {
800 uint32_t file_permissions = 0;
801 if (*this)
802 FileSystem::GetFilePermissions(*this, file_permissions);
803 return file_permissions;
Greg Claytonfbb76342013-11-20 21:07:01 +0000804}
805
Kate Stoneb9c1b512016-09-06 20:57:50 +0000806TimeValue FileSpec::GetModificationTime() const {
807 TimeValue mod_time;
808 struct stat file_stats;
809 if (GetFileStats(this, &file_stats))
810 mod_time.OffsetWithSeconds(file_stats.st_mtime);
811 return mod_time;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000812}
813
814//------------------------------------------------------------------
815// Directory string get accessor.
816//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000817ConstString &FileSpec::GetDirectory() { return m_directory; }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000818
819//------------------------------------------------------------------
820// Directory string const get accessor.
821//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000822const ConstString &FileSpec::GetDirectory() const { return m_directory; }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000823
824//------------------------------------------------------------------
825// Filename string get accessor.
826//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000827ConstString &FileSpec::GetFilename() { return m_filename; }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000828
829//------------------------------------------------------------------
830// Filename string const get accessor.
831//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000832const ConstString &FileSpec::GetFilename() const { return m_filename; }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000833
834//------------------------------------------------------------------
835// Extract the directory and path into a fixed buffer. This is
836// needed as the directory and path are stored in separate string
837// values.
838//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000839size_t FileSpec::GetPath(char *path, size_t path_max_len,
840 bool denormalize) const {
841 if (!path)
842 return 0;
Zachary Turnerb6d99242014-08-08 23:54:35 +0000843
Kate Stoneb9c1b512016-09-06 20:57:50 +0000844 std::string result = GetPath(denormalize);
845 ::snprintf(path, path_max_len, "%s", result.c_str());
846 return std::min(path_max_len - 1, result.length());
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000847}
848
Kate Stoneb9c1b512016-09-06 20:57:50 +0000849std::string FileSpec::GetPath(bool denormalize) const {
850 llvm::SmallString<64> result;
851 GetPath(result, denormalize);
852 return std::string(result.begin(), result.end());
Jason Molendaa7ae4672013-04-29 09:46:43 +0000853}
854
Kate Stoneb9c1b512016-09-06 20:57:50 +0000855const char *FileSpec::GetCString(bool denormalize) const {
856 return ConstString{GetPath(denormalize)}.AsCString(NULL);
Chaoren Lind3173f32015-05-29 19:52:29 +0000857}
858
Kate Stoneb9c1b512016-09-06 20:57:50 +0000859void FileSpec::GetPath(llvm::SmallVectorImpl<char> &path,
860 bool denormalize) const {
861 path.append(m_directory.GetStringRef().begin(),
862 m_directory.GetStringRef().end());
863 if (m_directory && m_filename &&
864 !IsPathSeparator(m_directory.GetStringRef().back(), m_syntax))
865 path.insert(path.end(), GetPrefferedPathSeparator(m_syntax));
866 path.append(m_filename.GetStringRef().begin(),
867 m_filename.GetStringRef().end());
868 Normalize(path, m_syntax);
869 if (denormalize && !path.empty())
870 Denormalize(path, m_syntax);
Zachary Turner4e8ddf52015-04-09 18:08:50 +0000871}
872
Kate Stoneb9c1b512016-09-06 20:57:50 +0000873ConstString FileSpec::GetFileNameExtension() const {
874 if (m_filename) {
Enrico Granataa9dbf432011-10-17 21:45:27 +0000875 const char *filename = m_filename.GetCString();
Kate Stoneb9c1b512016-09-06 20:57:50 +0000876 const char *dot_pos = strrchr(filename, '.');
877 if (dot_pos && dot_pos[1] != '\0')
878 return ConstString(dot_pos + 1);
879 }
880 return ConstString();
881}
882
883ConstString FileSpec::GetFileNameStrippingExtension() const {
884 const char *filename = m_filename.GetCString();
885 if (filename == NULL)
886 return ConstString();
887
888 const char *dot_pos = strrchr(filename, '.');
889 if (dot_pos == NULL)
890 return m_filename;
891
892 return ConstString(filename, dot_pos - filename);
Enrico Granataa9dbf432011-10-17 21:45:27 +0000893}
894
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000895//------------------------------------------------------------------
896// Returns a shared pointer to a data buffer that contains all or
897// part of the contents of a file. The data is memory mapped and
898// will lazily page in data from the file as memory is accessed.
Bruce Mitchenerd93c4a32014-07-01 21:22:11 +0000899// The data that is mapped will start "file_offset" bytes into the
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000900// file, and "file_size" bytes will be mapped. If "file_size" is
901// greater than the number of bytes available in the file starting
902// at "file_offset", the number of bytes will be appropriately
903// truncated. The final number of bytes that get mapped can be
904// verified using the DataBuffer::GetByteSize() function.
905//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000906DataBufferSP FileSpec::MemoryMapFileContents(off_t file_offset,
907 size_t file_size) const {
908 DataBufferSP data_sp;
909 std::unique_ptr<DataBufferMemoryMap> mmap_data(new DataBufferMemoryMap());
910 if (mmap_data.get()) {
911 const size_t mapped_length =
912 mmap_data->MemoryMapFromFileSpec(this, file_offset, file_size);
913 if (((file_size == SIZE_MAX) && (mapped_length > 0)) ||
914 (mapped_length >= file_size))
915 data_sp.reset(mmap_data.release());
916 }
917 return data_sp;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000918}
919
Kate Stoneb9c1b512016-09-06 20:57:50 +0000920DataBufferSP FileSpec::MemoryMapFileContentsIfLocal(off_t file_offset,
921 size_t file_size) const {
922 if (FileSystem::IsLocal(*this))
923 return MemoryMapFileContents(file_offset, file_size);
924 else
925 return ReadFileContents(file_offset, file_size, NULL);
Greg Clayton736888c2015-02-23 23:47:09 +0000926}
927
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000928//------------------------------------------------------------------
929// Return the size in bytes that this object takes in memory. This
930// returns the size in bytes of this object, not any shared string
931// values it may refer to.
932//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000933size_t FileSpec::MemorySize() const {
934 return m_filename.MemorySize() + m_directory.MemorySize();
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000935}
936
Kate Stoneb9c1b512016-09-06 20:57:50 +0000937size_t FileSpec::ReadFileContents(off_t file_offset, void *dst, size_t dst_len,
938 Error *error_ptr) const {
939 Error error;
940 size_t bytes_read = 0;
941 char resolved_path[PATH_MAX];
942 if (GetPath(resolved_path, sizeof(resolved_path))) {
943 File file;
944 error = file.Open(resolved_path, File::eOpenOptionRead);
945 if (error.Success()) {
946 off_t file_offset_after_seek = file_offset;
947 bytes_read = dst_len;
948 error = file.Read(dst, bytes_read, file_offset_after_seek);
Greg Claytondda4f7b2010-06-30 23:03:03 +0000949 }
Kate Stoneb9c1b512016-09-06 20:57:50 +0000950 } else {
951 error.SetErrorString("invalid file specification");
952 }
953 if (error_ptr)
954 *error_ptr = error;
955 return bytes_read;
Greg Claytondda4f7b2010-06-30 23:03:03 +0000956}
957
958//------------------------------------------------------------------
959// Returns a shared pointer to a data buffer that contains all or
960// part of the contents of a file. The data copies into a heap based
961// buffer that lives in the DataBuffer shared pointer object returned.
962// The data that is cached will start "file_offset" bytes into the
963// file, and "file_size" bytes will be mapped. If "file_size" is
964// greater than the number of bytes available in the file starting
965// at "file_offset", the number of bytes will be appropriately
966// truncated. The final number of bytes that get mapped can be
967// verified using the DataBuffer::GetByteSize() function.
968//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +0000969DataBufferSP FileSpec::ReadFileContents(off_t file_offset, size_t file_size,
970 Error *error_ptr) const {
971 Error error;
972 DataBufferSP data_sp;
973 char resolved_path[PATH_MAX];
974 if (GetPath(resolved_path, sizeof(resolved_path))) {
975 File file;
976 error = file.Open(resolved_path, File::eOpenOptionRead);
977 if (error.Success()) {
978 const bool null_terminate = false;
979 error = file.Read(file_size, file_offset, null_terminate, data_sp);
Greg Clayton0b0b5122012-08-30 18:15:10 +0000980 }
Kate Stoneb9c1b512016-09-06 20:57:50 +0000981 } else {
982 error.SetErrorString("invalid file specification");
983 }
984 if (error_ptr)
985 *error_ptr = error;
986 return data_sp;
Greg Clayton0b0b5122012-08-30 18:15:10 +0000987}
988
Kate Stoneb9c1b512016-09-06 20:57:50 +0000989DataBufferSP FileSpec::ReadFileContentsAsCString(Error *error_ptr) {
990 Error error;
991 DataBufferSP data_sp;
992 char resolved_path[PATH_MAX];
993 if (GetPath(resolved_path, sizeof(resolved_path))) {
994 File file;
995 error = file.Open(resolved_path, File::eOpenOptionRead);
996 if (error.Success()) {
997 off_t offset = 0;
998 size_t length = SIZE_MAX;
999 const bool null_terminate = true;
1000 error = file.Read(length, offset, null_terminate, data_sp);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001001 }
Kate Stoneb9c1b512016-09-06 20:57:50 +00001002 } else {
1003 error.SetErrorString("invalid file specification");
1004 }
1005 if (error_ptr)
1006 *error_ptr = error;
1007 return data_sp;
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001008}
1009
Kate Stoneb9c1b512016-09-06 20:57:50 +00001010size_t FileSpec::ReadFileLines(STLStringArray &lines) {
1011 lines.clear();
1012 char path[PATH_MAX];
1013 if (GetPath(path, sizeof(path))) {
1014 std::ifstream file_stream(path);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001015
Kate Stoneb9c1b512016-09-06 20:57:50 +00001016 if (file_stream) {
1017 std::string line;
1018 while (getline(file_stream, line))
1019 lines.push_back(line);
Greg Clayton58fc50e2010-10-20 22:52:05 +00001020 }
Kate Stoneb9c1b512016-09-06 20:57:50 +00001021 }
1022 return lines.size();
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001023}
Greg Clayton4272cc72011-02-02 02:24:04 +00001024
1025FileSpec::EnumerateDirectoryResult
Kate Stoneb9c1b512016-09-06 20:57:50 +00001026FileSpec::ForEachItemInDirectory(const char *dir_path,
1027 DirectoryCallback const &callback) {
1028 if (dir_path && dir_path[0]) {
Zachary Turner190fadc2016-03-22 17:58:09 +00001029#ifdef _WIN32
Kate Stoneb9c1b512016-09-06 20:57:50 +00001030 std::string szDir(dir_path);
1031 szDir += "\\*";
Greg Clayton58c65f02015-06-29 18:29:00 +00001032
Kate Stoneb9c1b512016-09-06 20:57:50 +00001033 std::wstring wszDir;
1034 if (!llvm::ConvertUTF8toWide(szDir, wszDir)) {
1035 return eEnumerateDirectoryResultNext;
Greg Clayton58c65f02015-06-29 18:29:00 +00001036 }
Kate Stoneb9c1b512016-09-06 20:57:50 +00001037
1038 WIN32_FIND_DATAW ffd;
1039 HANDLE hFind = FindFirstFileW(wszDir.c_str(), &ffd);
1040
1041 if (hFind == INVALID_HANDLE_VALUE) {
1042 return eEnumerateDirectoryResultNext;
1043 }
1044
1045 do {
1046 FileSpec::FileType file_type = eFileTypeUnknown;
1047 if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1048 size_t len = wcslen(ffd.cFileName);
1049
1050 if (len == 1 && ffd.cFileName[0] == L'.')
1051 continue;
1052
1053 if (len == 2 && ffd.cFileName[0] == L'.' && ffd.cFileName[1] == L'.')
1054 continue;
1055
1056 file_type = eFileTypeDirectory;
1057 } else if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) {
1058 file_type = eFileTypeOther;
1059 } else {
1060 file_type = eFileTypeRegular;
1061 }
1062
1063 std::string fileName;
1064 if (!llvm::convertWideToUTF8(ffd.cFileName, fileName)) {
1065 continue;
1066 }
1067
1068 std::vector<char> child_path(PATH_MAX);
1069 const int child_path_len =
1070 ::snprintf(child_path.data(), child_path.size(), "%s\\%s", dir_path,
1071 fileName.c_str());
1072 if (child_path_len < (int)(child_path.size() - 1)) {
1073 // Don't resolve the file type or path
1074 FileSpec child_path_spec(child_path.data(), false);
1075
1076 EnumerateDirectoryResult result = callback(file_type, child_path_spec);
1077
1078 switch (result) {
1079 case eEnumerateDirectoryResultNext:
1080 // Enumerate next entry in the current directory. We just
1081 // exit this switch and will continue enumerating the
1082 // current directory as we currently are...
1083 break;
1084
1085 case eEnumerateDirectoryResultEnter: // Recurse into the current entry
1086 // if it is a directory or symlink,
1087 // or next if not
1088 if (FileSpec::ForEachItemInDirectory(child_path.data(), callback) ==
1089 eEnumerateDirectoryResultQuit) {
1090 // The subdirectory returned Quit, which means to
1091 // stop all directory enumerations at all levels.
1092 return eEnumerateDirectoryResultQuit;
1093 }
1094 break;
1095
1096 case eEnumerateDirectoryResultExit: // Exit from the current directory
1097 // at the current level.
1098 // Exit from this directory level and tell parent to
1099 // keep enumerating.
1100 return eEnumerateDirectoryResultNext;
1101
1102 case eEnumerateDirectoryResultQuit: // Stop directory enumerations at
1103 // any level
1104 return eEnumerateDirectoryResultQuit;
1105 }
1106 }
1107 } while (FindNextFileW(hFind, &ffd) != 0);
1108
1109 FindClose(hFind);
1110#else
1111 lldb_utility::CleanUp<DIR *, int> dir_path_dir(opendir(dir_path), NULL,
1112 closedir);
1113 if (dir_path_dir.is_valid()) {
1114 char dir_path_last_char = dir_path[strlen(dir_path) - 1];
1115
1116 long path_max = fpathconf(dirfd(dir_path_dir.get()), _PC_NAME_MAX);
1117#if defined(__APPLE_) && defined(__DARWIN_MAXPATHLEN)
1118 if (path_max < __DARWIN_MAXPATHLEN)
1119 path_max = __DARWIN_MAXPATHLEN;
1120#endif
1121 struct dirent *buf, *dp;
1122 buf = (struct dirent *)malloc(offsetof(struct dirent, d_name) + path_max +
1123 1);
1124
1125 while (buf && readdir_r(dir_path_dir.get(), buf, &dp) == 0 && dp) {
1126 // Only search directories
1127 if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN) {
1128 size_t len = strlen(dp->d_name);
1129
1130 if (len == 1 && dp->d_name[0] == '.')
1131 continue;
1132
1133 if (len == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.')
1134 continue;
1135 }
1136
1137 FileSpec::FileType file_type = eFileTypeUnknown;
1138
1139 switch (dp->d_type) {
1140 default:
1141 case DT_UNKNOWN:
1142 file_type = eFileTypeUnknown;
1143 break;
1144 case DT_FIFO:
1145 file_type = eFileTypePipe;
1146 break;
1147 case DT_CHR:
1148 file_type = eFileTypeOther;
1149 break;
1150 case DT_DIR:
1151 file_type = eFileTypeDirectory;
1152 break;
1153 case DT_BLK:
1154 file_type = eFileTypeOther;
1155 break;
1156 case DT_REG:
1157 file_type = eFileTypeRegular;
1158 break;
1159 case DT_LNK:
1160 file_type = eFileTypeSymbolicLink;
1161 break;
1162 case DT_SOCK:
1163 file_type = eFileTypeSocket;
1164 break;
1165#if !defined(__OpenBSD__)
1166 case DT_WHT:
1167 file_type = eFileTypeOther;
1168 break;
1169#endif
1170 }
1171
1172 char child_path[PATH_MAX];
1173
1174 // Don't make paths with "/foo//bar", that just confuses everybody.
1175 int child_path_len;
1176 if (dir_path_last_char == '/')
1177 child_path_len = ::snprintf(child_path, sizeof(child_path), "%s%s",
1178 dir_path, dp->d_name);
1179 else
1180 child_path_len = ::snprintf(child_path, sizeof(child_path), "%s/%s",
1181 dir_path, dp->d_name);
1182
1183 if (child_path_len < (int)(sizeof(child_path) - 1)) {
1184 // Don't resolve the file type or path
1185 FileSpec child_path_spec(child_path, false);
1186
1187 EnumerateDirectoryResult result =
1188 callback(file_type, child_path_spec);
1189
1190 switch (result) {
1191 case eEnumerateDirectoryResultNext:
1192 // Enumerate next entry in the current directory. We just
1193 // exit this switch and will continue enumerating the
1194 // current directory as we currently are...
1195 break;
1196
1197 case eEnumerateDirectoryResultEnter: // Recurse into the current entry
1198 // if it is a directory or
1199 // symlink, or next if not
1200 if (FileSpec::ForEachItemInDirectory(child_path, callback) ==
1201 eEnumerateDirectoryResultQuit) {
1202 // The subdirectory returned Quit, which means to
1203 // stop all directory enumerations at all levels.
1204 if (buf)
1205 free(buf);
1206 return eEnumerateDirectoryResultQuit;
1207 }
1208 break;
1209
1210 case eEnumerateDirectoryResultExit: // Exit from the current directory
1211 // at the current level.
1212 // Exit from this directory level and tell parent to
1213 // keep enumerating.
1214 if (buf)
1215 free(buf);
1216 return eEnumerateDirectoryResultNext;
1217
1218 case eEnumerateDirectoryResultQuit: // Stop directory enumerations at
1219 // any level
1220 if (buf)
1221 free(buf);
1222 return eEnumerateDirectoryResultQuit;
1223 }
1224 }
1225 }
1226 if (buf) {
1227 free(buf);
1228 }
1229 }
1230#endif
1231 }
1232 // By default when exiting a directory, we tell the parent enumeration
1233 // to continue enumerating.
1234 return eEnumerateDirectoryResultNext;
Greg Clayton58c65f02015-06-29 18:29:00 +00001235}
1236
1237FileSpec::EnumerateDirectoryResult
Kate Stoneb9c1b512016-09-06 20:57:50 +00001238FileSpec::EnumerateDirectory(const char *dir_path, bool find_directories,
1239 bool find_files, bool find_other,
1240 EnumerateDirectoryCallbackType callback,
1241 void *callback_baton) {
1242 return ForEachItemInDirectory(
1243 dir_path,
1244 [&find_directories, &find_files, &find_other, &callback,
1245 &callback_baton](FileType file_type, const FileSpec &file_spec) {
1246 switch (file_type) {
1247 case FileType::eFileTypeDirectory:
1248 if (find_directories)
1249 return callback(callback_baton, file_type, file_spec);
1250 break;
1251 case FileType::eFileTypeRegular:
1252 if (find_files)
1253 return callback(callback_baton, file_type, file_spec);
1254 break;
1255 default:
1256 if (find_other)
1257 return callback(callback_baton, file_type, file_spec);
1258 break;
Daniel Maleae0f8f572013-08-26 23:57:52 +00001259 }
Kate Stoneb9c1b512016-09-06 20:57:50 +00001260 return eEnumerateDirectoryResultNext;
1261 });
Daniel Maleae0f8f572013-08-26 23:57:52 +00001262}
1263
Kate Stoneb9c1b512016-09-06 20:57:50 +00001264FileSpec FileSpec::CopyByAppendingPathComponent(const char *new_path) const {
1265 FileSpec ret = *this;
1266 ret.AppendPathComponent(new_path);
1267 return ret;
Chaoren Lin0c5a9c12015-06-05 00:28:06 +00001268}
1269
Kate Stoneb9c1b512016-09-06 20:57:50 +00001270FileSpec FileSpec::CopyByRemovingLastPathComponent() const {
1271 const bool resolve = false;
1272 if (m_filename.IsEmpty() && m_directory.IsEmpty())
1273 return FileSpec("", resolve);
1274 if (m_directory.IsEmpty())
1275 return FileSpec("", resolve);
1276 if (m_filename.IsEmpty()) {
1277 const char *dir_cstr = m_directory.GetCString();
1278 const char *last_slash_ptr = ::strrchr(dir_cstr, '/');
1279
1280 // check for obvious cases before doing the full thing
1281 if (!last_slash_ptr)
1282 return FileSpec("", resolve);
1283 if (last_slash_ptr == dir_cstr)
1284 return FileSpec("/", resolve);
1285
1286 size_t last_slash_pos = last_slash_ptr - dir_cstr + 1;
1287 ConstString new_path(dir_cstr, last_slash_pos);
1288 return FileSpec(new_path.GetCString(), resolve);
1289 } else
1290 return FileSpec(m_directory.GetCString(), resolve);
Chaoren Lin0c5a9c12015-06-05 00:28:06 +00001291}
1292
Kate Stoneb9c1b512016-09-06 20:57:50 +00001293ConstString FileSpec::GetLastPathComponent() const {
1294 if (m_filename)
1295 return m_filename;
1296 if (m_directory) {
1297 const char *dir_cstr = m_directory.GetCString();
1298 const char *last_slash_ptr = ::strrchr(dir_cstr, '/');
1299 if (last_slash_ptr == NULL)
1300 return m_directory;
1301 if (last_slash_ptr == dir_cstr) {
1302 if (last_slash_ptr[1] == 0)
1303 return ConstString(last_slash_ptr);
1304 else
1305 return ConstString(last_slash_ptr + 1);
1306 }
1307 if (last_slash_ptr[1] != 0)
1308 return ConstString(last_slash_ptr + 1);
1309 const char *penultimate_slash_ptr = last_slash_ptr;
1310 while (*penultimate_slash_ptr) {
1311 --penultimate_slash_ptr;
1312 if (penultimate_slash_ptr == dir_cstr)
1313 break;
1314 if (*penultimate_slash_ptr == '/')
1315 break;
1316 }
1317 ConstString result(penultimate_slash_ptr + 1,
1318 last_slash_ptr - penultimate_slash_ptr);
1319 return result;
1320 }
1321 return ConstString();
Chaoren Lin0c5a9c12015-06-05 00:28:06 +00001322}
1323
Kate Stoneb9c1b512016-09-06 20:57:50 +00001324void FileSpec::PrependPathComponent(const char *new_path) {
1325 if (!new_path)
1326 return;
1327 const bool resolve = false;
1328 if (m_filename.IsEmpty() && m_directory.IsEmpty()) {
1329 SetFile(new_path, resolve);
1330 return;
1331 }
1332 StreamString stream;
1333 if (m_filename.IsEmpty())
1334 stream.Printf("%s/%s", new_path, m_directory.GetCString());
1335 else if (m_directory.IsEmpty())
1336 stream.Printf("%s/%s", new_path, m_filename.GetCString());
1337 else
1338 stream.Printf("%s/%s/%s", new_path, m_directory.GetCString(),
1339 m_filename.GetCString());
1340 SetFile(stream.GetData(), resolve);
Daniel Maleae0f8f572013-08-26 23:57:52 +00001341}
1342
Kate Stoneb9c1b512016-09-06 20:57:50 +00001343void FileSpec::PrependPathComponent(const std::string &new_path) {
1344 return PrependPathComponent(new_path.c_str());
Chaoren Lind3173f32015-05-29 19:52:29 +00001345}
1346
Kate Stoneb9c1b512016-09-06 20:57:50 +00001347void FileSpec::PrependPathComponent(const FileSpec &new_path) {
1348 return PrependPathComponent(new_path.GetPath(false));
Chaoren Lin0c5a9c12015-06-05 00:28:06 +00001349}
1350
Kate Stoneb9c1b512016-09-06 20:57:50 +00001351void FileSpec::AppendPathComponent(const char *new_path) {
1352 if (!new_path)
1353 return;
1354
1355 StreamString stream;
1356 if (!m_directory.IsEmpty()) {
1357 stream.PutCString(m_directory.GetCString());
1358 if (!IsPathSeparator(m_directory.GetStringRef().back(), m_syntax))
1359 stream.PutChar(GetPrefferedPathSeparator(m_syntax));
1360 }
1361
1362 if (!m_filename.IsEmpty()) {
1363 stream.PutCString(m_filename.GetCString());
1364 if (!IsPathSeparator(m_filename.GetStringRef().back(), m_syntax))
1365 stream.PutChar(GetPrefferedPathSeparator(m_syntax));
1366 }
1367
1368 while (IsPathSeparator(new_path[0], m_syntax))
1369 new_path++;
1370
1371 stream.PutCString(new_path);
1372
1373 const bool resolve = false;
1374 SetFile(stream.GetData(), resolve, m_syntax);
1375}
1376
1377void FileSpec::AppendPathComponent(const std::string &new_path) {
1378 return AppendPathComponent(new_path.c_str());
1379}
1380
1381void FileSpec::AppendPathComponent(const FileSpec &new_path) {
1382 return AppendPathComponent(new_path.GetPath(false));
1383}
1384
1385void FileSpec::RemoveLastPathComponent() {
1386 const bool resolve = false;
1387 if (m_filename.IsEmpty() && m_directory.IsEmpty()) {
1388 SetFile("", resolve);
1389 return;
1390 }
1391 if (m_directory.IsEmpty()) {
1392 SetFile("", resolve);
1393 return;
1394 }
1395 if (m_filename.IsEmpty()) {
1396 const char *dir_cstr = m_directory.GetCString();
1397 const char *last_slash_ptr = ::strrchr(dir_cstr, '/');
1398
1399 // check for obvious cases before doing the full thing
1400 if (!last_slash_ptr) {
1401 SetFile("", resolve);
1402 return;
Daniel Maleae0f8f572013-08-26 23:57:52 +00001403 }
Kate Stoneb9c1b512016-09-06 20:57:50 +00001404 if (last_slash_ptr == dir_cstr) {
1405 SetFile("/", resolve);
1406 return;
Daniel Maleae0f8f572013-08-26 23:57:52 +00001407 }
Kate Stoneb9c1b512016-09-06 20:57:50 +00001408 size_t last_slash_pos = last_slash_ptr - dir_cstr + 1;
1409 ConstString new_path(dir_cstr, last_slash_pos);
1410 SetFile(new_path.GetCString(), resolve);
1411 } else
1412 SetFile(m_directory.GetCString(), resolve);
Daniel Maleae0f8f572013-08-26 23:57:52 +00001413}
Greg Clayton1f746072012-08-29 21:13:06 +00001414//------------------------------------------------------------------
1415/// Returns true if the filespec represents an implementation source
1416/// file (files with a ".c", ".cpp", ".m", ".mm" (many more)
1417/// extension).
1418///
1419/// @return
1420/// \b true if the filespec represents an implementation source
1421/// file, \b false otherwise.
1422//------------------------------------------------------------------
Kate Stoneb9c1b512016-09-06 20:57:50 +00001423bool FileSpec::IsSourceImplementationFile() const {
1424 ConstString extension(GetFileNameExtension());
1425 if (extension) {
1426 static RegularExpression g_source_file_regex(
1427 "^([cC]|[mM]|[mM][mM]|[cC][pP][pP]|[cC]\\+\\+|[cC][xX][xX]|[cC][cC]|["
1428 "cC][pP]|[sS]|[aA][sS][mM]|[fF]|[fF]77|[fF]90|[fF]95|[fF]03|[fF][oO]["
1429 "rR]|[fF][tT][nN]|[fF][pP][pP]|[aA][dD][aA]|[aA][dD][bB]|[aA][dD][sS])"
1430 "$");
1431 return g_source_file_regex.Execute(extension.GetCString());
1432 }
1433 return false;
Greg Clayton1f746072012-08-29 21:13:06 +00001434}
1435
Kate Stoneb9c1b512016-09-06 20:57:50 +00001436bool FileSpec::IsRelative() const {
1437 const char *dir = m_directory.GetCString();
1438 llvm::StringRef directory(dir ? dir : "");
Zachary Turner270e99a2014-12-08 21:36:42 +00001439
Kate Stoneb9c1b512016-09-06 20:57:50 +00001440 if (directory.size() > 0) {
1441 if (PathSyntaxIsPosix(m_syntax)) {
1442 // If the path doesn't start with '/' or '~', return true
1443 switch (directory[0]) {
1444 case '/':
1445 case '~':
1446 return false;
1447 default:
Greg Claytona0ca6602012-10-18 16:33:33 +00001448 return true;
Kate Stoneb9c1b512016-09-06 20:57:50 +00001449 }
1450 } else {
1451 if (directory.size() >= 2 && directory[1] == ':')
1452 return false;
1453 if (directory[0] == '/')
1454 return false;
1455 return true;
Greg Claytona0ca6602012-10-18 16:33:33 +00001456 }
Kate Stoneb9c1b512016-09-06 20:57:50 +00001457 } else if (m_filename) {
1458 // No directory, just a basename, return true
1459 return true;
1460 }
1461 return false;
Greg Claytona0ca6602012-10-18 16:33:33 +00001462}
Chaoren Lin372e9062015-06-09 17:54:27 +00001463
Kate Stoneb9c1b512016-09-06 20:57:50 +00001464bool FileSpec::IsAbsolute() const { return !FileSpec::IsRelative(); }