blob: 66818453bc0b6f5765542de9cc309a752d627fa3 [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
10
Virgile Bellob2f1fb22013-08-23 12:44:05 +000011#ifndef _WIN32
Greg Clayton4272cc72011-02-02 02:24:04 +000012#include <dirent.h>
Virgile Bellob2f1fb22013-08-23 12:44:05 +000013#else
14#include "lldb/Host/windows/windows.h"
15#endif
Chris Lattner30fdc8d2010-06-08 16:52:24 +000016#include <fcntl.h>
Chris Lattner30fdc8d2010-06-08 16:52:24 +000017#include <libgen.h>
Chris Lattner30fdc8d2010-06-08 16:52:24 +000018#include <sys/stat.h>
Rafael Espindola09079162013-06-13 20:10:23 +000019#include <set>
Greg Claytone0f3c022011-02-07 17:41:11 +000020#include <string.h>
Jim Ingham9035e7c2011-02-07 19:42:39 +000021#include <fstream>
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
Caroline Tice391a9602010-09-12 00:10:52 +000028#include "llvm/ADT/StringRef.h"
Greg Clayton38a61402010-12-02 23:20:03 +000029#include "llvm/Support/Path.h"
30#include "llvm/Support/Program.h"
Caroline Tice391a9602010-09-12 00:10:52 +000031
Daniel Maleae0f8f572013-08-26 23:57:52 +000032#include "lldb/Core/StreamString.h"
Greg Clayton96c09682012-01-04 22:56:43 +000033#include "lldb/Host/File.h"
Greg Clayton53239f02011-02-08 05:05:52 +000034#include "lldb/Host/FileSpec.h"
Chris Lattner30fdc8d2010-06-08 16:52:24 +000035#include "lldb/Core/DataBufferHeap.h"
36#include "lldb/Core/DataBufferMemoryMap.h"
Greg Clayton1f746072012-08-29 21:13:06 +000037#include "lldb/Core/RegularExpression.h"
Chris Lattner30fdc8d2010-06-08 16:52:24 +000038#include "lldb/Core/Stream.h"
Caroline Tice428a9a52010-09-10 04:48:55 +000039#include "lldb/Host/Host.h"
Greg Clayton4272cc72011-02-02 02:24:04 +000040#include "lldb/Utility/CleanUp.h"
Chris Lattner30fdc8d2010-06-08 16:52:24 +000041
42using namespace lldb;
43using namespace lldb_private;
Chris Lattner30fdc8d2010-06-08 16:52:24 +000044
45static bool
46GetFileStats (const FileSpec *file_spec, struct stat *stats_ptr)
47{
48 char resolved_path[PATH_MAX];
Greg Clayton7e14f912011-04-23 02:04:55 +000049 if (file_spec->GetPath (resolved_path, sizeof(resolved_path)))
Chris Lattner30fdc8d2010-06-08 16:52:24 +000050 return ::stat (resolved_path, stats_ptr) == 0;
51 return false;
52}
53
Greg Clayton45319462011-02-08 00:35:34 +000054#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER
Greg Claytonfd184262011-02-05 02:27:52 +000055
Chris Lattner30fdc8d2010-06-08 16:52:24 +000056static const char*
57GetCachedGlobTildeSlash()
58{
59 static std::string g_tilde;
60 if (g_tilde.empty())
61 {
Jim Inghamf818ca32010-07-01 01:48:53 +000062 struct passwd *user_entry;
63 user_entry = getpwuid(geteuid());
64 if (user_entry != NULL)
65 g_tilde = user_entry->pw_dir;
66
Chris Lattner30fdc8d2010-06-08 16:52:24 +000067 if (g_tilde.empty())
68 return NULL;
69 }
70 return g_tilde.c_str();
71}
72
Greg Clayton87e5ff02011-02-08 05:19:06 +000073#endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER
74
Jim Inghamf818ca32010-07-01 01:48:53 +000075// Resolves the username part of a path of the form ~user/other/directories, and
76// writes the result into dst_path.
77// Returns 0 if there WAS a ~ in the path but the username couldn't be resolved.
78// Otherwise returns the number of characters copied into dst_path. If the return
79// is >= dst_len, then the resolved path is too long...
Greg Claytonc982c762010-07-09 20:39:50 +000080size_t
Jim Inghamf818ca32010-07-01 01:48:53 +000081FileSpec::ResolveUsername (const char *src_path, char *dst_path, size_t dst_len)
82{
Greg Clayton87e5ff02011-02-08 05:19:06 +000083 if (src_path == NULL || src_path[0] == '\0')
84 return 0;
85
86#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER
87
Jim Inghamf818ca32010-07-01 01:48:53 +000088 char user_home[PATH_MAX];
89 const char *user_name;
90
Greg Claytona5d24f62010-07-01 17:07:48 +000091
Jim Inghamf818ca32010-07-01 01:48:53 +000092 // If there's no ~, then just copy src_path straight to dst_path (they may be the same string...)
93 if (src_path[0] != '~')
94 {
Greg Claytonc982c762010-07-09 20:39:50 +000095 size_t len = strlen (src_path);
Jim Inghamf818ca32010-07-01 01:48:53 +000096 if (len >= dst_len)
97 {
Greg Claytona5d24f62010-07-01 17:07:48 +000098 ::bcopy (src_path, dst_path, dst_len - 1);
99 dst_path[dst_len] = '\0';
Jim Inghamf818ca32010-07-01 01:48:53 +0000100 }
101 else
Greg Claytona5d24f62010-07-01 17:07:48 +0000102 ::bcopy (src_path, dst_path, len + 1);
103
Jim Inghamf818ca32010-07-01 01:48:53 +0000104 return len;
105 }
106
Eli Friedmanfeaeebf2010-07-02 19:15:50 +0000107 const char *first_slash = ::strchr (src_path, '/');
Jim Inghamf818ca32010-07-01 01:48:53 +0000108 char remainder[PATH_MAX];
109
110 if (first_slash == NULL)
111 {
112 // The whole name is the username (minus the ~):
113 user_name = src_path + 1;
114 remainder[0] = '\0';
115 }
116 else
117 {
Greg Claytonc7bece562013-01-25 18:06:21 +0000118 size_t user_name_len = first_slash - src_path - 1;
Greg Claytona5d24f62010-07-01 17:07:48 +0000119 ::memcpy (user_home, src_path + 1, user_name_len);
Jim Inghamf818ca32010-07-01 01:48:53 +0000120 user_home[user_name_len] = '\0';
121 user_name = user_home;
122
Greg Claytona5d24f62010-07-01 17:07:48 +0000123 ::strcpy (remainder, first_slash);
Jim Inghamf818ca32010-07-01 01:48:53 +0000124 }
Greg Claytona5d24f62010-07-01 17:07:48 +0000125
Jim Inghamf818ca32010-07-01 01:48:53 +0000126 if (user_name == NULL)
127 return 0;
128 // User name of "" means the current user...
129
130 struct passwd *user_entry;
Greg Claytonc982c762010-07-09 20:39:50 +0000131 const char *home_dir = NULL;
Jim Inghamf818ca32010-07-01 01:48:53 +0000132
133 if (user_name[0] == '\0')
134 {
135 home_dir = GetCachedGlobTildeSlash();
136 }
137 else
138 {
Greg Claytona5d24f62010-07-01 17:07:48 +0000139 user_entry = ::getpwnam (user_name);
Jim Inghamf818ca32010-07-01 01:48:53 +0000140 if (user_entry != NULL)
141 home_dir = user_entry->pw_dir;
142 }
143
144 if (home_dir == NULL)
145 return 0;
146 else
147 return ::snprintf (dst_path, dst_len, "%s%s", home_dir, remainder);
Greg Clayton87e5ff02011-02-08 05:19:06 +0000148#else
149 // Resolving home directories is not supported, just copy the path...
150 return ::snprintf (dst_path, dst_len, "%s", src_path);
151#endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER
Jim Inghamf818ca32010-07-01 01:48:53 +0000152}
153
Greg Claytonc982c762010-07-09 20:39:50 +0000154size_t
Jim Ingham84363072011-02-08 23:24:09 +0000155FileSpec::ResolvePartialUsername (const char *partial_name, StringList &matches)
156{
157#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER
158 size_t extant_entries = matches.GetSize();
159
160 setpwent();
161 struct passwd *user_entry;
162 const char *name_start = partial_name + 1;
163 std::set<std::string> name_list;
164
165 while ((user_entry = getpwent()) != NULL)
166 {
167 if (strstr(user_entry->pw_name, name_start) == user_entry->pw_name)
168 {
169 std::string tmp_buf("~");
170 tmp_buf.append(user_entry->pw_name);
171 tmp_buf.push_back('/');
172 name_list.insert(tmp_buf);
173 }
174 }
175 std::set<std::string>::iterator pos, end = name_list.end();
176 for (pos = name_list.begin(); pos != end; pos++)
177 {
178 matches.AppendString((*pos).c_str());
179 }
180 return matches.GetSize() - extant_entries;
181#else
182 // Resolving home directories is not supported, just copy the path...
183 return 0;
184#endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER
185}
186
187
188
189size_t
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000190FileSpec::Resolve (const char *src_path, char *dst_path, size_t dst_len)
191{
192 if (src_path == NULL || src_path[0] == '\0')
193 return 0;
194
195 // Glob if needed for ~/, otherwise copy in case src_path is same as dst_path...
196 char unglobbed_path[PATH_MAX];
Greg Clayton45319462011-02-08 00:35:34 +0000197#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER
Jim Inghamf818ca32010-07-01 01:48:53 +0000198 if (src_path[0] == '~')
199 {
Greg Claytonc982c762010-07-09 20:39:50 +0000200 size_t return_count = ResolveUsername(src_path, unglobbed_path, sizeof(unglobbed_path));
Jim Inghamf818ca32010-07-01 01:48:53 +0000201
202 // If we couldn't find the user referred to, or the resultant path was too long,
203 // then just copy over the src_path.
204 if (return_count == 0 || return_count >= sizeof(unglobbed_path))
205 ::snprintf (unglobbed_path, sizeof(unglobbed_path), "%s", src_path);
206 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000207 else
Greg Clayton45319462011-02-08 00:35:34 +0000208#endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER
Greg Claytonfd184262011-02-05 02:27:52 +0000209 {
210 ::snprintf(unglobbed_path, sizeof(unglobbed_path), "%s", src_path);
211 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000212
213 // Now resolve the path if needed
214 char resolved_path[PATH_MAX];
215 if (::realpath (unglobbed_path, resolved_path))
216 {
217 // Success, copy the resolved path
218 return ::snprintf(dst_path, dst_len, "%s", resolved_path);
219 }
220 else
221 {
222 // Failed, just copy the unglobbed path
223 return ::snprintf(dst_path, dst_len, "%s", unglobbed_path);
224 }
225}
226
227FileSpec::FileSpec() :
228 m_directory(),
229 m_filename()
230{
231}
232
233//------------------------------------------------------------------
234// Default constructor that can take an optional full path to a
235// file on disk.
236//------------------------------------------------------------------
Jim Ingham0909e5f2010-09-16 00:57:33 +0000237FileSpec::FileSpec(const char *pathname, bool resolve_path) :
238 m_directory(),
Greg Clayton7481c202010-11-08 00:28:40 +0000239 m_filename(),
240 m_is_resolved(false)
Jim Ingham0909e5f2010-09-16 00:57:33 +0000241{
242 if (pathname && pathname[0])
243 SetFile(pathname, resolve_path);
244}
245
246//------------------------------------------------------------------
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000247// Copy constructor
248//------------------------------------------------------------------
249FileSpec::FileSpec(const FileSpec& rhs) :
250 m_directory (rhs.m_directory),
Greg Clayton7481c202010-11-08 00:28:40 +0000251 m_filename (rhs.m_filename),
252 m_is_resolved (rhs.m_is_resolved)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000253{
254}
255
256//------------------------------------------------------------------
257// Copy constructor
258//------------------------------------------------------------------
259FileSpec::FileSpec(const FileSpec* rhs) :
260 m_directory(),
261 m_filename()
262{
263 if (rhs)
264 *this = *rhs;
265}
266
267//------------------------------------------------------------------
268// Virtual destrcuctor in case anyone inherits from this class.
269//------------------------------------------------------------------
270FileSpec::~FileSpec()
271{
272}
273
274//------------------------------------------------------------------
275// Assignment operator.
276//------------------------------------------------------------------
277const FileSpec&
278FileSpec::operator= (const FileSpec& rhs)
279{
280 if (this != &rhs)
281 {
282 m_directory = rhs.m_directory;
283 m_filename = rhs.m_filename;
Greg Clayton7481c202010-11-08 00:28:40 +0000284 m_is_resolved = rhs.m_is_resolved;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000285 }
286 return *this;
287}
288
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000289//------------------------------------------------------------------
290// Update the contents of this object with a new path. The path will
291// be split up into a directory and filename and stored as uniqued
292// string values for quick comparison and efficient memory usage.
293//------------------------------------------------------------------
294void
Greg Clayton7481c202010-11-08 00:28:40 +0000295FileSpec::SetFile (const char *pathname, bool resolve)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000296{
297 m_filename.Clear();
298 m_directory.Clear();
Greg Clayton7481c202010-11-08 00:28:40 +0000299 m_is_resolved = false;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000300 if (pathname == NULL || pathname[0] == '\0')
301 return;
302
303 char resolved_path[PATH_MAX];
Jim Ingham0909e5f2010-09-16 00:57:33 +0000304 bool path_fit = true;
305
306 if (resolve)
307 {
308 path_fit = (FileSpec::Resolve (pathname, resolved_path, sizeof(resolved_path)) < sizeof(resolved_path) - 1);
Greg Clayton7481c202010-11-08 00:28:40 +0000309 m_is_resolved = path_fit;
Jim Ingham0909e5f2010-09-16 00:57:33 +0000310 }
311 else
312 {
Greg Clayton7481c202010-11-08 00:28:40 +0000313 // Copy the path because "basename" and "dirname" want to muck with the
314 // path buffer
315 if (::strlen (pathname) > sizeof(resolved_path) - 1)
Jim Ingham0909e5f2010-09-16 00:57:33 +0000316 path_fit = false;
317 else
Greg Clayton7481c202010-11-08 00:28:40 +0000318 ::strcpy (resolved_path, pathname);
Jim Ingham0909e5f2010-09-16 00:57:33 +0000319 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000320
Jim Ingham0909e5f2010-09-16 00:57:33 +0000321
322 if (path_fit)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000323 {
324 char *filename = ::basename (resolved_path);
325 if (filename)
326 {
327 m_filename.SetCString (filename);
328 // Truncate the basename off the end of the resolved path
329
330 // Only attempt to get the dirname if it looks like we have a path
331 if (strchr(resolved_path, '/'))
332 {
333 char *directory = ::dirname (resolved_path);
334
335 // Make sure we didn't get our directory resolved to "." without having
336 // specified
337 if (directory)
338 m_directory.SetCString(directory);
339 else
340 {
341 char *last_resolved_path_slash = strrchr(resolved_path, '/');
342 if (last_resolved_path_slash)
343 {
344 *last_resolved_path_slash = '\0';
345 m_directory.SetCString(resolved_path);
346 }
347 }
348 }
349 }
350 else
351 m_directory.SetCString(resolved_path);
352 }
353}
354
355//----------------------------------------------------------------------
356// Convert to pointer operator. This allows code to check any FileSpec
357// objects to see if they contain anything valid using code such as:
358//
359// if (file_spec)
360// {}
361//----------------------------------------------------------------------
Greg Clayton6372d1c2011-09-12 04:00:42 +0000362FileSpec::operator bool() const
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000363{
Greg Clayton6372d1c2011-09-12 04:00:42 +0000364 return m_filename || m_directory;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000365}
366
367//----------------------------------------------------------------------
368// Logical NOT operator. This allows code to check any FileSpec
369// objects to see if they are invalid using code such as:
370//
371// if (!file_spec)
372// {}
373//----------------------------------------------------------------------
374bool
375FileSpec::operator!() const
376{
377 return !m_directory && !m_filename;
378}
379
380//------------------------------------------------------------------
381// Equal to operator
382//------------------------------------------------------------------
383bool
384FileSpec::operator== (const FileSpec& rhs) const
385{
Greg Clayton7481c202010-11-08 00:28:40 +0000386 if (m_filename == rhs.m_filename)
387 {
388 if (m_directory == rhs.m_directory)
389 return true;
390
391 // TODO: determine if we want to keep this code in here.
392 // The code below was added to handle a case where we were
393 // trying to set a file and line breakpoint and one path
394 // was resolved, and the other not and the directory was
395 // in a mount point that resolved to a more complete path:
396 // "/tmp/a.c" == "/private/tmp/a.c". I might end up pulling
397 // this out...
398 if (IsResolved() && rhs.IsResolved())
399 {
400 // Both paths are resolved, no need to look further...
401 return false;
402 }
403
404 FileSpec resolved_lhs(*this);
405
406 // If "this" isn't resolved, resolve it
407 if (!IsResolved())
408 {
409 if (resolved_lhs.ResolvePath())
410 {
411 // This path wasn't resolved but now it is. Check if the resolved
412 // directory is the same as our unresolved directory, and if so,
413 // we can mark this object as resolved to avoid more future resolves
414 m_is_resolved = (m_directory == resolved_lhs.m_directory);
415 }
416 else
417 return false;
418 }
419
420 FileSpec resolved_rhs(rhs);
421 if (!rhs.IsResolved())
422 {
423 if (resolved_rhs.ResolvePath())
424 {
425 // rhs's path wasn't resolved but now it is. Check if the resolved
426 // directory is the same as rhs's unresolved directory, and if so,
427 // we can mark this object as resolved to avoid more future resolves
Jim Inghama537f6c2012-11-03 02:12:46 +0000428 rhs.m_is_resolved = (rhs.m_directory == resolved_rhs.m_directory);
Greg Clayton7481c202010-11-08 00:28:40 +0000429 }
430 else
431 return false;
432 }
433
434 // If we reach this point in the code we were able to resolve both paths
435 // and since we only resolve the paths if the basenames are equal, then
436 // we can just check if both directories are equal...
437 return resolved_lhs.GetDirectory() == resolved_rhs.GetDirectory();
438 }
439 return false;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000440}
441
442//------------------------------------------------------------------
443// Not equal to operator
444//------------------------------------------------------------------
445bool
446FileSpec::operator!= (const FileSpec& rhs) const
447{
Greg Clayton7481c202010-11-08 00:28:40 +0000448 return !(*this == rhs);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000449}
450
451//------------------------------------------------------------------
452// Less than operator
453//------------------------------------------------------------------
454bool
455FileSpec::operator< (const FileSpec& rhs) const
456{
457 return FileSpec::Compare(*this, rhs, true) < 0;
458}
459
460//------------------------------------------------------------------
461// Dump a FileSpec object to a stream
462//------------------------------------------------------------------
463Stream&
464lldb_private::operator << (Stream &s, const FileSpec& f)
465{
466 f.Dump(&s);
467 return s;
468}
469
470//------------------------------------------------------------------
471// Clear this object by releasing both the directory and filename
472// string values and making them both the empty string.
473//------------------------------------------------------------------
474void
475FileSpec::Clear()
476{
477 m_directory.Clear();
478 m_filename.Clear();
479}
480
481//------------------------------------------------------------------
482// Compare two FileSpec objects. If "full" is true, then both
483// the directory and the filename must match. If "full" is false,
484// then the directory names for "a" and "b" are only compared if
485// they are both non-empty. This allows a FileSpec object to only
486// contain a filename and it can match FileSpec objects that have
487// matching filenames with different paths.
488//
489// Return -1 if the "a" is less than "b", 0 if "a" is equal to "b"
490// and "1" if "a" is greater than "b".
491//------------------------------------------------------------------
492int
493FileSpec::Compare(const FileSpec& a, const FileSpec& b, bool full)
494{
495 int result = 0;
496
497 // If full is true, then we must compare both the directory and filename.
498
499 // If full is false, then if either directory is empty, then we match on
500 // the basename only, and if both directories have valid values, we still
501 // do a full compare. This allows for matching when we just have a filename
502 // in one of the FileSpec objects.
503
504 if (full || (a.m_directory && b.m_directory))
505 {
506 result = ConstString::Compare(a.m_directory, b.m_directory);
507 if (result)
508 return result;
509 }
510 return ConstString::Compare (a.m_filename, b.m_filename);
511}
512
513bool
514FileSpec::Equal (const FileSpec& a, const FileSpec& b, bool full)
515{
Jim Ingham87df91b2011-09-23 00:54:11 +0000516 if (!full && (a.GetDirectory().IsEmpty() || b.GetDirectory().IsEmpty()))
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000517 return a.m_filename == b.m_filename;
Jim Ingham87df91b2011-09-23 00:54:11 +0000518 else
519 return a == b;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000520}
521
522
523
524//------------------------------------------------------------------
525// Dump the object to the supplied stream. If the object contains
526// a valid directory name, it will be displayed followed by a
527// directory delimiter, and the filename.
528//------------------------------------------------------------------
529void
530FileSpec::Dump(Stream *s) const
531{
Jason Molendadb7d11c2013-05-06 10:21:11 +0000532 static ConstString g_slash_only ("/");
Enrico Granata80fcdd42012-11-03 00:09:46 +0000533 if (s)
534 {
535 m_directory.Dump(s);
Jason Molendadb7d11c2013-05-06 10:21:11 +0000536 if (m_directory && m_directory != g_slash_only)
Enrico Granata80fcdd42012-11-03 00:09:46 +0000537 s->PutChar('/');
538 m_filename.Dump(s);
539 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000540}
541
542//------------------------------------------------------------------
543// Returns true if the file exists.
544//------------------------------------------------------------------
545bool
546FileSpec::Exists () const
547{
548 struct stat file_stats;
549 return GetFileStats (this, &file_stats);
550}
551
Caroline Tice428a9a52010-09-10 04:48:55 +0000552bool
553FileSpec::ResolveExecutableLocation ()
554{
Greg Clayton274060b2010-10-20 20:54:39 +0000555 if (!m_directory)
Caroline Tice391a9602010-09-12 00:10:52 +0000556 {
Greg Clayton58f41712011-01-25 21:32:01 +0000557 const char *file_cstr = m_filename.GetCString();
558 if (file_cstr)
Caroline Tice391a9602010-09-12 00:10:52 +0000559 {
Greg Clayton58f41712011-01-25 21:32:01 +0000560 const std::string file_str (file_cstr);
Rafael Espindolaff89ff22013-06-13 19:25:41 +0000561 std::string path = llvm::sys::FindProgramByName (file_str);
562 llvm::StringRef dir_ref = llvm::sys::path::parent_path(path);
Greg Clayton58f41712011-01-25 21:32:01 +0000563 //llvm::StringRef dir_ref = path.getDirname();
564 if (! dir_ref.empty())
Caroline Tice391a9602010-09-12 00:10:52 +0000565 {
Greg Clayton58f41712011-01-25 21:32:01 +0000566 // FindProgramByName returns "." if it can't find the file.
567 if (strcmp (".", dir_ref.data()) == 0)
568 return false;
569
570 m_directory.SetCString (dir_ref.data());
571 if (Exists())
Caroline Tice391a9602010-09-12 00:10:52 +0000572 return true;
Greg Clayton58f41712011-01-25 21:32:01 +0000573 else
574 {
575 // If FindProgramByName found the file, it returns the directory + filename in its return results.
576 // We need to separate them.
577 FileSpec tmp_file (dir_ref.data(), false);
578 if (tmp_file.Exists())
579 {
580 m_directory = tmp_file.m_directory;
581 return true;
582 }
Caroline Tice391a9602010-09-12 00:10:52 +0000583 }
584 }
585 }
586 }
587
588 return false;
Caroline Tice428a9a52010-09-10 04:48:55 +0000589}
590
Jim Ingham0909e5f2010-09-16 00:57:33 +0000591bool
592FileSpec::ResolvePath ()
593{
Greg Clayton7481c202010-11-08 00:28:40 +0000594 if (m_is_resolved)
595 return true; // We have already resolved this path
596
597 char path_buf[PATH_MAX];
Jim Ingham0909e5f2010-09-16 00:57:33 +0000598 if (!GetPath (path_buf, PATH_MAX))
599 return false;
Greg Clayton7481c202010-11-08 00:28:40 +0000600 // SetFile(...) will set m_is_resolved correctly if it can resolve the path
Jim Ingham0909e5f2010-09-16 00:57:33 +0000601 SetFile (path_buf, true);
Greg Clayton7481c202010-11-08 00:28:40 +0000602 return m_is_resolved;
Jim Ingham0909e5f2010-09-16 00:57:33 +0000603}
604
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000605uint64_t
606FileSpec::GetByteSize() const
607{
608 struct stat file_stats;
609 if (GetFileStats (this, &file_stats))
610 return file_stats.st_size;
611 return 0;
612}
613
614FileSpec::FileType
615FileSpec::GetFileType () const
616{
617 struct stat file_stats;
618 if (GetFileStats (this, &file_stats))
619 {
620 mode_t file_type = file_stats.st_mode & S_IFMT;
621 switch (file_type)
622 {
623 case S_IFDIR: return eFileTypeDirectory;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000624 case S_IFREG: return eFileTypeRegular;
Virgile Bellob2f1fb22013-08-23 12:44:05 +0000625#ifndef _WIN32
626 case S_IFIFO: return eFileTypePipe;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000627 case S_IFSOCK: return eFileTypeSocket;
628 case S_IFLNK: return eFileTypeSymbolicLink;
Virgile Bellob2f1fb22013-08-23 12:44:05 +0000629#endif
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000630 default:
631 break;
632 }
Greg Clayton4272cc72011-02-02 02:24:04 +0000633 return eFileTypeUnknown;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000634 }
635 return eFileTypeInvalid;
636}
637
638TimeValue
639FileSpec::GetModificationTime () const
640{
641 TimeValue mod_time;
642 struct stat file_stats;
643 if (GetFileStats (this, &file_stats))
Eli Friedman6abb6342010-06-11 04:52:22 +0000644 mod_time.OffsetWithSeconds(file_stats.st_mtime);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000645 return mod_time;
646}
647
648//------------------------------------------------------------------
649// Directory string get accessor.
650//------------------------------------------------------------------
651ConstString &
652FileSpec::GetDirectory()
653{
654 return m_directory;
655}
656
657//------------------------------------------------------------------
658// Directory string const get accessor.
659//------------------------------------------------------------------
660const ConstString &
661FileSpec::GetDirectory() const
662{
663 return m_directory;
664}
665
666//------------------------------------------------------------------
667// Filename string get accessor.
668//------------------------------------------------------------------
669ConstString &
670FileSpec::GetFilename()
671{
672 return m_filename;
673}
674
675//------------------------------------------------------------------
676// Filename string const get accessor.
677//------------------------------------------------------------------
678const ConstString &
679FileSpec::GetFilename() const
680{
681 return m_filename;
682}
683
684//------------------------------------------------------------------
685// Extract the directory and path into a fixed buffer. This is
686// needed as the directory and path are stored in separate string
687// values.
688//------------------------------------------------------------------
Greg Claytoncfd1ace2010-10-31 03:01:06 +0000689size_t
690FileSpec::GetPath(char *path, size_t path_max_len) const
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000691{
Greg Claytoncfd1ace2010-10-31 03:01:06 +0000692 if (path_max_len)
Greg Claytondd36def2010-10-17 22:03:32 +0000693 {
Greg Claytoncfd1ace2010-10-31 03:01:06 +0000694 const char *dirname = m_directory.GetCString();
695 const char *filename = m_filename.GetCString();
Greg Claytondd36def2010-10-17 22:03:32 +0000696 if (dirname)
697 {
698 if (filename)
Greg Claytoncfd1ace2010-10-31 03:01:06 +0000699 return ::snprintf (path, path_max_len, "%s/%s", dirname, filename);
Greg Claytondd36def2010-10-17 22:03:32 +0000700 else
Greg Claytoncfd1ace2010-10-31 03:01:06 +0000701 return ::snprintf (path, path_max_len, "%s", dirname);
Greg Claytondd36def2010-10-17 22:03:32 +0000702 }
703 else if (filename)
704 {
Greg Claytoncfd1ace2010-10-31 03:01:06 +0000705 return ::snprintf (path, path_max_len, "%s", filename);
Greg Claytondd36def2010-10-17 22:03:32 +0000706 }
707 }
Enrico Granataa9dbf432011-10-17 21:45:27 +0000708 if (path)
709 path[0] = '\0';
Greg Claytoncfd1ace2010-10-31 03:01:06 +0000710 return 0;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000711}
712
Greg Claytona44c1e62013-04-29 16:36:27 +0000713std::string
714FileSpec::GetPath (void) const
Jason Molendaa7ae4672013-04-29 09:46:43 +0000715{
Jason Molendadb7d11c2013-05-06 10:21:11 +0000716 static ConstString g_slash_only ("/");
Greg Claytona44c1e62013-04-29 16:36:27 +0000717 std::string path;
Jason Molendaa7ae4672013-04-29 09:46:43 +0000718 const char *dirname = m_directory.GetCString();
719 const char *filename = m_filename.GetCString();
Jason Molendaa7ae4672013-04-29 09:46:43 +0000720 if (dirname)
721 {
722 path.append (dirname);
Jason Molendadb7d11c2013-05-06 10:21:11 +0000723 if (filename && m_directory != g_slash_only)
Jason Molendaa7ae4672013-04-29 09:46:43 +0000724 path.append ("/");
725 }
726 if (filename)
Jason Molendaa7ae4672013-04-29 09:46:43 +0000727 path.append (filename);
Jason Molendaa7ae4672013-04-29 09:46:43 +0000728 return path;
729}
730
Enrico Granataa9dbf432011-10-17 21:45:27 +0000731ConstString
732FileSpec::GetFileNameExtension () const
733{
Greg Clayton1f746072012-08-29 21:13:06 +0000734 if (m_filename)
735 {
736 const char *filename = m_filename.GetCString();
737 const char* dot_pos = strrchr(filename, '.');
738 if (dot_pos && dot_pos[1] != '\0')
739 return ConstString(dot_pos+1);
740 }
741 return ConstString();
Enrico Granataa9dbf432011-10-17 21:45:27 +0000742}
743
744ConstString
745FileSpec::GetFileNameStrippingExtension () const
746{
747 const char *filename = m_filename.GetCString();
748 if (filename == NULL)
749 return ConstString();
750
Johnny Chenf5df5372011-10-18 19:28:30 +0000751 const char* dot_pos = strrchr(filename, '.');
Enrico Granataa9dbf432011-10-17 21:45:27 +0000752 if (dot_pos == NULL)
753 return m_filename;
754
755 return ConstString(filename, dot_pos-filename);
756}
757
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000758//------------------------------------------------------------------
759// Returns a shared pointer to a data buffer that contains all or
760// part of the contents of a file. The data is memory mapped and
761// will lazily page in data from the file as memory is accessed.
762// The data that is mappped will start "file_offset" bytes into the
763// file, and "file_size" bytes will be mapped. If "file_size" is
764// greater than the number of bytes available in the file starting
765// at "file_offset", the number of bytes will be appropriately
766// truncated. The final number of bytes that get mapped can be
767// verified using the DataBuffer::GetByteSize() function.
768//------------------------------------------------------------------
769DataBufferSP
770FileSpec::MemoryMapFileContents(off_t file_offset, size_t file_size) const
771{
772 DataBufferSP data_sp;
Greg Clayton7b0992d2013-04-18 22:45:39 +0000773 std::unique_ptr<DataBufferMemoryMap> mmap_data(new DataBufferMemoryMap());
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000774 if (mmap_data.get())
775 {
Greg Claytond398a1c2013-04-20 00:23:26 +0000776 const size_t mapped_length = mmap_data->MemoryMapFromFileSpec (this, file_offset, file_size);
777 if (((file_size == SIZE_MAX) && (mapped_length > 0)) || (mapped_length >= file_size))
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000778 data_sp.reset(mmap_data.release());
779 }
780 return data_sp;
781}
782
783
784//------------------------------------------------------------------
785// Return the size in bytes that this object takes in memory. This
786// returns the size in bytes of this object, not any shared string
787// values it may refer to.
788//------------------------------------------------------------------
789size_t
790FileSpec::MemorySize() const
791{
792 return m_filename.MemorySize() + m_directory.MemorySize();
793}
794
Greg Claytondda4f7b2010-06-30 23:03:03 +0000795
796size_t
Greg Clayton4017fa32012-01-06 02:01:06 +0000797FileSpec::ReadFileContents (off_t file_offset, void *dst, size_t dst_len, Error *error_ptr) const
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000798{
Greg Clayton4017fa32012-01-06 02:01:06 +0000799 Error error;
Greg Claytondda4f7b2010-06-30 23:03:03 +0000800 size_t bytes_read = 0;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000801 char resolved_path[PATH_MAX];
802 if (GetPath(resolved_path, sizeof(resolved_path)))
803 {
Greg Clayton96c09682012-01-04 22:56:43 +0000804 File file;
805 error = file.Open(resolved_path, File::eOpenOptionRead);
806 if (error.Success())
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000807 {
Greg Clayton96c09682012-01-04 22:56:43 +0000808 off_t file_offset_after_seek = file_offset;
809 bytes_read = dst_len;
810 error = file.Read(dst, bytes_read, file_offset_after_seek);
Greg Claytondda4f7b2010-06-30 23:03:03 +0000811 }
Greg Claytondda4f7b2010-06-30 23:03:03 +0000812 }
Greg Clayton4017fa32012-01-06 02:01:06 +0000813 else
814 {
815 error.SetErrorString("invalid file specification");
816 }
817 if (error_ptr)
818 *error_ptr = error;
Greg Claytondda4f7b2010-06-30 23:03:03 +0000819 return bytes_read;
820}
821
822//------------------------------------------------------------------
823// Returns a shared pointer to a data buffer that contains all or
824// part of the contents of a file. The data copies into a heap based
825// buffer that lives in the DataBuffer shared pointer object returned.
826// The data that is cached will start "file_offset" bytes into the
827// file, and "file_size" bytes will be mapped. If "file_size" is
828// greater than the number of bytes available in the file starting
829// at "file_offset", the number of bytes will be appropriately
830// truncated. The final number of bytes that get mapped can be
831// verified using the DataBuffer::GetByteSize() function.
832//------------------------------------------------------------------
833DataBufferSP
Greg Clayton4017fa32012-01-06 02:01:06 +0000834FileSpec::ReadFileContents (off_t file_offset, size_t file_size, Error *error_ptr) const
Greg Claytondda4f7b2010-06-30 23:03:03 +0000835{
Greg Clayton4017fa32012-01-06 02:01:06 +0000836 Error error;
Greg Claytondda4f7b2010-06-30 23:03:03 +0000837 DataBufferSP data_sp;
838 char resolved_path[PATH_MAX];
839 if (GetPath(resolved_path, sizeof(resolved_path)))
840 {
Greg Clayton96c09682012-01-04 22:56:43 +0000841 File file;
842 error = file.Open(resolved_path, File::eOpenOptionRead);
843 if (error.Success())
Greg Clayton0b0b5122012-08-30 18:15:10 +0000844 {
845 const bool null_terminate = false;
846 error = file.Read (file_size, file_offset, null_terminate, data_sp);
847 }
848 }
849 else
850 {
851 error.SetErrorString("invalid file specification");
852 }
853 if (error_ptr)
854 *error_ptr = error;
855 return data_sp;
856}
857
858DataBufferSP
859FileSpec::ReadFileContentsAsCString(Error *error_ptr)
860{
861 Error error;
862 DataBufferSP data_sp;
863 char resolved_path[PATH_MAX];
864 if (GetPath(resolved_path, sizeof(resolved_path)))
865 {
866 File file;
867 error = file.Open(resolved_path, File::eOpenOptionRead);
868 if (error.Success())
869 {
870 off_t offset = 0;
871 size_t length = SIZE_MAX;
872 const bool null_terminate = true;
873 error = file.Read (length, offset, null_terminate, data_sp);
874 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000875 }
Greg Clayton4017fa32012-01-06 02:01:06 +0000876 else
877 {
878 error.SetErrorString("invalid file specification");
879 }
880 if (error_ptr)
881 *error_ptr = error;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000882 return data_sp;
883}
884
Greg Clayton58fc50e2010-10-20 22:52:05 +0000885size_t
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000886FileSpec::ReadFileLines (STLStringArray &lines)
887{
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000888 lines.clear();
Greg Clayton58fc50e2010-10-20 22:52:05 +0000889 char path[PATH_MAX];
890 if (GetPath(path, sizeof(path)))
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000891 {
Greg Claytone01e07b2013-04-18 18:10:51 +0000892 std::ifstream file_stream (path);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000893
Greg Clayton58fc50e2010-10-20 22:52:05 +0000894 if (file_stream)
895 {
896 std::string line;
897 while (getline (file_stream, line))
898 lines.push_back (line);
899 }
900 }
901 return lines.size();
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000902}
Greg Clayton4272cc72011-02-02 02:24:04 +0000903
904FileSpec::EnumerateDirectoryResult
905FileSpec::EnumerateDirectory
906(
907 const char *dir_path,
908 bool find_directories,
909 bool find_files,
910 bool find_other,
911 EnumerateDirectoryCallbackType callback,
912 void *callback_baton
913)
914{
915 if (dir_path && dir_path[0])
916 {
Virgile Bellob2f1fb22013-08-23 12:44:05 +0000917#if _WIN32
918 char szDir[MAX_PATH];
919 strcpy_s(szDir, MAX_PATH, dir_path);
920 strcat_s(szDir, MAX_PATH, "\\*");
921
922 WIN32_FIND_DATA ffd;
923 HANDLE hFind = FindFirstFile(szDir, &ffd);
924
925 if (hFind == INVALID_HANDLE_VALUE)
926 {
927 return eEnumerateDirectoryResultNext;
928 }
929
930 do
931 {
932 bool call_callback = false;
933 FileSpec::FileType file_type = eFileTypeUnknown;
934 if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
935 {
936 size_t len = strlen(ffd.cFileName);
937
938 if (len == 1 && ffd.cFileName[0] == '.')
939 continue;
940
941 if (len == 2 && ffd.cFileName[0] == '.' && ffd.cFileName[1] == '.')
942 continue;
943
944 file_type = eFileTypeDirectory;
945 call_callback = find_directories;
946 }
947 else if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE)
948 {
949 file_type = eFileTypeOther;
950 call_callback = find_other;
951 }
952 else
953 {
954 file_type = eFileTypeRegular;
955 call_callback = find_files;
956 }
957 if (call_callback)
958 {
959 char child_path[MAX_PATH];
960 const int child_path_len = ::snprintf (child_path, sizeof(child_path), "%s\\%s", dir_path, ffd.cFileName);
961 if (child_path_len < (int)(sizeof(child_path) - 1))
962 {
963 // Don't resolve the file type or path
964 FileSpec child_path_spec (child_path, false);
965
966 EnumerateDirectoryResult result = callback (callback_baton, file_type, child_path_spec);
967
968 switch (result)
969 {
970 case eEnumerateDirectoryResultNext:
971 // Enumerate next entry in the current directory. We just
972 // exit this switch and will continue enumerating the
973 // current directory as we currently are...
974 break;
975
976 case eEnumerateDirectoryResultEnter: // Recurse into the current entry if it is a directory or symlink, or next if not
977 if (FileSpec::EnumerateDirectory(child_path,
978 find_directories,
979 find_files,
980 find_other,
981 callback,
982 callback_baton) == eEnumerateDirectoryResultQuit)
983 {
984 // The subdirectory returned Quit, which means to
985 // stop all directory enumerations at all levels.
986 return eEnumerateDirectoryResultQuit;
987 }
988 break;
989
990 case eEnumerateDirectoryResultExit: // Exit from the current directory at the current level.
991 // Exit from this directory level and tell parent to
992 // keep enumerating.
993 return eEnumerateDirectoryResultNext;
994
995 case eEnumerateDirectoryResultQuit: // Stop directory enumerations at any level
996 return eEnumerateDirectoryResultQuit;
997 }
998 }
999 }
1000 } while (FindNextFile(hFind, &ffd) != 0);
1001
1002 FindClose(hFind);
1003#else
1004 lldb_utility::CleanUp <DIR *, int> dir_path_dir(opendir(dir_path), NULL, closedir);
Greg Clayton4272cc72011-02-02 02:24:04 +00001005 if (dir_path_dir.is_valid())
1006 {
Jason Molenda14aef122013-04-04 03:19:27 +00001007 long path_max = fpathconf (dirfd (dir_path_dir.get()), _PC_NAME_MAX);
1008#if defined (__APPLE_) && defined (__DARWIN_MAXPATHLEN)
1009 if (path_max < __DARWIN_MAXPATHLEN)
1010 path_max = __DARWIN_MAXPATHLEN;
1011#endif
1012 struct dirent *buf, *dp;
1013 buf = (struct dirent *) malloc (offsetof (struct dirent, d_name) + path_max + 1);
1014
1015 while (buf && readdir_r(dir_path_dir.get(), buf, &dp) == 0 && dp)
Greg Clayton4272cc72011-02-02 02:24:04 +00001016 {
1017 // Only search directories
1018 if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN)
1019 {
Greg Claytone0f3c022011-02-07 17:41:11 +00001020 size_t len = strlen(dp->d_name);
1021
1022 if (len == 1 && dp->d_name[0] == '.')
Greg Clayton4272cc72011-02-02 02:24:04 +00001023 continue;
1024
Greg Claytone0f3c022011-02-07 17:41:11 +00001025 if (len == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.')
Greg Clayton4272cc72011-02-02 02:24:04 +00001026 continue;
1027 }
1028
1029 bool call_callback = false;
1030 FileSpec::FileType file_type = eFileTypeUnknown;
1031
1032 switch (dp->d_type)
1033 {
1034 default:
1035 case DT_UNKNOWN: file_type = eFileTypeUnknown; call_callback = true; break;
1036 case DT_FIFO: file_type = eFileTypePipe; call_callback = find_other; break;
1037 case DT_CHR: file_type = eFileTypeOther; call_callback = find_other; break;
1038 case DT_DIR: file_type = eFileTypeDirectory; call_callback = find_directories; break;
1039 case DT_BLK: file_type = eFileTypeOther; call_callback = find_other; break;
1040 case DT_REG: file_type = eFileTypeRegular; call_callback = find_files; break;
1041 case DT_LNK: file_type = eFileTypeSymbolicLink; call_callback = find_other; break;
1042 case DT_SOCK: file_type = eFileTypeSocket; call_callback = find_other; break;
Greg Clayton2b4d9b72011-04-01 18:18:34 +00001043#if !defined(__OpenBSD__)
Greg Clayton4272cc72011-02-02 02:24:04 +00001044 case DT_WHT: file_type = eFileTypeOther; call_callback = find_other; break;
Greg Clayton2b4d9b72011-04-01 18:18:34 +00001045#endif
Greg Clayton4272cc72011-02-02 02:24:04 +00001046 }
1047
1048 if (call_callback)
1049 {
1050 char child_path[PATH_MAX];
1051 const int child_path_len = ::snprintf (child_path, sizeof(child_path), "%s/%s", dir_path, dp->d_name);
Johnny Chen44805302011-07-19 19:48:13 +00001052 if (child_path_len < (int)(sizeof(child_path) - 1))
Greg Clayton4272cc72011-02-02 02:24:04 +00001053 {
1054 // Don't resolve the file type or path
1055 FileSpec child_path_spec (child_path, false);
1056
1057 EnumerateDirectoryResult result = callback (callback_baton, file_type, child_path_spec);
1058
1059 switch (result)
1060 {
Greg Clayton4272cc72011-02-02 02:24:04 +00001061 case eEnumerateDirectoryResultNext:
1062 // Enumerate next entry in the current directory. We just
1063 // exit this switch and will continue enumerating the
1064 // current directory as we currently are...
1065 break;
1066
1067 case eEnumerateDirectoryResultEnter: // Recurse into the current entry if it is a directory or symlink, or next if not
1068 if (FileSpec::EnumerateDirectory (child_path,
1069 find_directories,
1070 find_files,
1071 find_other,
1072 callback,
1073 callback_baton) == eEnumerateDirectoryResultQuit)
1074 {
1075 // The subdirectory returned Quit, which means to
1076 // stop all directory enumerations at all levels.
Jim Ingham5c42d8a2013-05-15 18:27:08 +00001077 if (buf)
1078 free (buf);
Greg Clayton4272cc72011-02-02 02:24:04 +00001079 return eEnumerateDirectoryResultQuit;
1080 }
1081 break;
1082
1083 case eEnumerateDirectoryResultExit: // Exit from the current directory at the current level.
1084 // Exit from this directory level and tell parent to
1085 // keep enumerating.
Jason Molendafe806902013-05-04 00:39:52 +00001086 if (buf)
1087 free (buf);
Greg Clayton4272cc72011-02-02 02:24:04 +00001088 return eEnumerateDirectoryResultNext;
1089
1090 case eEnumerateDirectoryResultQuit: // Stop directory enumerations at any level
Jason Molendafe806902013-05-04 00:39:52 +00001091 if (buf)
1092 free (buf);
Greg Clayton4272cc72011-02-02 02:24:04 +00001093 return eEnumerateDirectoryResultQuit;
1094 }
1095 }
1096 }
1097 }
Jason Molenda14aef122013-04-04 03:19:27 +00001098 if (buf)
1099 {
1100 free (buf);
1101 }
Greg Clayton4272cc72011-02-02 02:24:04 +00001102 }
Virgile Bellob2f1fb22013-08-23 12:44:05 +00001103#endif
Greg Clayton4272cc72011-02-02 02:24:04 +00001104 }
1105 // By default when exiting a directory, we tell the parent enumeration
1106 // to continue enumerating.
1107 return eEnumerateDirectoryResultNext;
1108}
1109
Daniel Maleae0f8f572013-08-26 23:57:52 +00001110FileSpec
1111FileSpec::CopyByAppendingPathComponent (const char *new_path) const
1112{
1113 const bool resolve = false;
1114 if (m_filename.IsEmpty() && m_directory.IsEmpty())
1115 return FileSpec(new_path,resolve);
1116 StreamString stream;
1117 if (m_filename.IsEmpty())
1118 stream.Printf("%s/%s",m_directory.GetCString(),new_path);
1119 else if (m_directory.IsEmpty())
1120 stream.Printf("%s/%s",m_filename.GetCString(),new_path);
1121 else
1122 stream.Printf("%s/%s/%s",m_directory.GetCString(), m_filename.GetCString(),new_path);
1123 return FileSpec(stream.GetData(),resolve);
1124}
1125
1126FileSpec
1127FileSpec::CopyByRemovingLastPathComponent () const
1128{
1129 const bool resolve = false;
1130 if (m_filename.IsEmpty() && m_directory.IsEmpty())
1131 return FileSpec("",resolve);
1132 if (m_directory.IsEmpty())
1133 return FileSpec("",resolve);
1134 if (m_filename.IsEmpty())
1135 {
1136 const char* dir_cstr = m_directory.GetCString();
1137 const char* last_slash_ptr = ::strrchr(dir_cstr, '/');
1138
1139 // check for obvious cases before doing the full thing
1140 if (!last_slash_ptr)
1141 return FileSpec("",resolve);
1142 if (last_slash_ptr == dir_cstr)
1143 return FileSpec("/",resolve);
1144
1145 size_t last_slash_pos = last_slash_ptr - dir_cstr+1;
1146 ConstString new_path(dir_cstr,last_slash_pos);
1147 return FileSpec(new_path.GetCString(),resolve);
1148 }
1149 else
1150 return FileSpec(m_directory.GetCString(),resolve);
1151}
1152
1153const char*
1154FileSpec::GetLastPathComponent () const
1155{
1156 if (m_filename.IsEmpty() && m_directory.IsEmpty())
1157 return NULL;
1158 if (m_filename.IsEmpty())
1159 {
1160 const char* dir_cstr = m_directory.GetCString();
1161 const char* last_slash_ptr = ::strrchr(dir_cstr, '/');
1162 if (last_slash_ptr == NULL)
1163 return m_directory.GetCString();
1164 if (last_slash_ptr == dir_cstr)
1165 {
1166 if (last_slash_ptr[1] == 0)
1167 return last_slash_ptr;
1168 else
1169 return last_slash_ptr+1;
1170 }
1171 if (last_slash_ptr[1] != 0)
1172 return last_slash_ptr+1;
1173 const char* penultimate_slash_ptr = last_slash_ptr;
1174 while (*penultimate_slash_ptr)
1175 {
1176 --penultimate_slash_ptr;
1177 if (penultimate_slash_ptr == dir_cstr)
1178 break;
1179 if (*penultimate_slash_ptr == '/')
1180 break;
1181 }
1182 ConstString new_path(penultimate_slash_ptr+1,last_slash_ptr-penultimate_slash_ptr);
1183 return new_path.AsCString();
1184 }
1185 return m_filename.GetCString();
1186}
1187
1188void
1189FileSpec::AppendPathComponent (const char *new_path)
1190{
1191 const bool resolve = false;
1192 if (m_filename.IsEmpty() && m_directory.IsEmpty())
1193 {
1194 SetFile(new_path,resolve);
1195 return;
1196 }
1197 StreamString stream;
1198 if (m_filename.IsEmpty())
1199 stream.Printf("%s/%s",m_directory.GetCString(),new_path);
1200 else if (m_directory.IsEmpty())
1201 stream.Printf("%s/%s",m_filename.GetCString(),new_path);
1202 else
1203 stream.Printf("%s/%s/%s",m_directory.GetCString(), m_filename.GetCString(),new_path);
1204 SetFile(stream.GetData(), resolve);
1205}
1206
1207void
1208FileSpec::RemoveLastPathComponent ()
1209{
1210 const bool resolve = false;
1211 if (m_filename.IsEmpty() && m_directory.IsEmpty())
1212 {
1213 SetFile("",resolve);
1214 return;
1215 }
1216 if (m_directory.IsEmpty())
1217 {
1218 SetFile("",resolve);
1219 return;
1220 }
1221 if (m_filename.IsEmpty())
1222 {
1223 const char* dir_cstr = m_directory.GetCString();
1224 const char* last_slash_ptr = ::strrchr(dir_cstr, '/');
1225
1226 // check for obvious cases before doing the full thing
1227 if (!last_slash_ptr)
1228 {
1229 SetFile("",resolve);
1230 return;
1231 }
1232 if (last_slash_ptr == dir_cstr)
1233 {
1234 SetFile("/",resolve);
1235 return;
1236 }
1237 size_t last_slash_pos = last_slash_ptr - dir_cstr+1;
1238 ConstString new_path(dir_cstr,last_slash_pos);
1239 SetFile(new_path.GetCString(),resolve);
1240 }
1241 else
1242 SetFile(m_directory.GetCString(),resolve);
1243}
Greg Clayton1f746072012-08-29 21:13:06 +00001244//------------------------------------------------------------------
1245/// Returns true if the filespec represents an implementation source
1246/// file (files with a ".c", ".cpp", ".m", ".mm" (many more)
1247/// extension).
1248///
1249/// @return
1250/// \b true if the filespec represents an implementation source
1251/// file, \b false otherwise.
1252//------------------------------------------------------------------
1253bool
1254FileSpec::IsSourceImplementationFile () const
1255{
1256 ConstString extension (GetFileNameExtension());
1257 if (extension)
1258 {
1259 static RegularExpression g_source_file_regex ("^(c|m|mm|cpp|c\\+\\+|cxx|cc|cp|s|asm|f|f77|f90|f95|f03|for|ftn|fpp|ada|adb|ads)$",
1260 REG_EXTENDED | REG_ICASE);
1261 return g_source_file_regex.Execute (extension.GetCString());
1262 }
1263 return false;
1264}
1265
Greg Claytona0ca6602012-10-18 16:33:33 +00001266bool
1267FileSpec::IsRelativeToCurrentWorkingDirectory () const
1268{
1269 const char *directory = m_directory.GetCString();
1270 if (directory && directory[0])
1271 {
1272 // If the path doesn't start with '/' or '~', return true
1273 switch (directory[0])
1274 {
1275 case '/':
1276 case '~':
1277 return false;
1278 default:
1279 return true;
1280 }
1281 }
1282 else if (m_filename)
1283 {
1284 // No directory, just a basename, return true
1285 return true;
1286 }
1287 return false;
1288}