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