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