blob: f1850439bc1366845c6fd726e3a5fabab202af21 [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>
Chris Lattner30fdc8d2010-06-08 16:52:24 +000012#include <libgen.h>
13#include <stdlib.h>
14#include <sys/param.h>
15#include <sys/stat.h>
16#include <sys/types.h>
Jim Inghamf818ca32010-07-01 01:48:53 +000017#include <pwd.h>
Chris Lattner30fdc8d2010-06-08 16:52:24 +000018
19#include <fstream>
20
Caroline Tice391a9602010-09-12 00:10:52 +000021#include "llvm/ADT/StringRef.h"
Greg Clayton38a61402010-12-02 23:20:03 +000022#include "llvm/Support/Path.h"
23#include "llvm/Support/Program.h"
Caroline Tice391a9602010-09-12 00:10:52 +000024
Chris Lattner30fdc8d2010-06-08 16:52:24 +000025#include "lldb/Core/FileSpec.h"
26#include "lldb/Core/DataBufferHeap.h"
27#include "lldb/Core/DataBufferMemoryMap.h"
28#include "lldb/Core/Stream.h"
Caroline Tice428a9a52010-09-10 04:48:55 +000029#include "lldb/Host/Host.h"
Chris Lattner30fdc8d2010-06-08 16:52:24 +000030
31using namespace lldb;
32using namespace lldb_private;
33using namespace std;
34
35static bool
36GetFileStats (const FileSpec *file_spec, struct stat *stats_ptr)
37{
38 char resolved_path[PATH_MAX];
39 if (file_spec->GetPath(&resolved_path[0], sizeof(resolved_path)))
40 return ::stat (resolved_path, stats_ptr) == 0;
41 return false;
42}
43
44static const char*
45GetCachedGlobTildeSlash()
46{
47 static std::string g_tilde;
48 if (g_tilde.empty())
49 {
Jim Inghamf818ca32010-07-01 01:48:53 +000050 struct passwd *user_entry;
51 user_entry = getpwuid(geteuid());
52 if (user_entry != NULL)
53 g_tilde = user_entry->pw_dir;
54
Chris Lattner30fdc8d2010-06-08 16:52:24 +000055 if (g_tilde.empty())
56 return NULL;
57 }
58 return g_tilde.c_str();
59}
60
Jim Inghamf818ca32010-07-01 01:48:53 +000061// Resolves the username part of a path of the form ~user/other/directories, and
62// writes the result into dst_path.
63// Returns 0 if there WAS a ~ in the path but the username couldn't be resolved.
64// Otherwise returns the number of characters copied into dst_path. If the return
65// is >= dst_len, then the resolved path is too long...
Greg Claytonc982c762010-07-09 20:39:50 +000066size_t
Jim Inghamf818ca32010-07-01 01:48:53 +000067FileSpec::ResolveUsername (const char *src_path, char *dst_path, size_t dst_len)
68{
69 char user_home[PATH_MAX];
70 const char *user_name;
71
72 if (src_path == NULL || src_path[0] == '\0')
73 return 0;
Greg Claytona5d24f62010-07-01 17:07:48 +000074
Jim Inghamf818ca32010-07-01 01:48:53 +000075 // If there's no ~, then just copy src_path straight to dst_path (they may be the same string...)
76 if (src_path[0] != '~')
77 {
Greg Claytonc982c762010-07-09 20:39:50 +000078 size_t len = strlen (src_path);
Jim Inghamf818ca32010-07-01 01:48:53 +000079 if (len >= dst_len)
80 {
Greg Claytona5d24f62010-07-01 17:07:48 +000081 ::bcopy (src_path, dst_path, dst_len - 1);
82 dst_path[dst_len] = '\0';
Jim Inghamf818ca32010-07-01 01:48:53 +000083 }
84 else
Greg Claytona5d24f62010-07-01 17:07:48 +000085 ::bcopy (src_path, dst_path, len + 1);
86
Jim Inghamf818ca32010-07-01 01:48:53 +000087 return len;
88 }
89
Eli Friedmanfeaeebf2010-07-02 19:15:50 +000090 const char *first_slash = ::strchr (src_path, '/');
Jim Inghamf818ca32010-07-01 01:48:53 +000091 char remainder[PATH_MAX];
92
93 if (first_slash == NULL)
94 {
95 // The whole name is the username (minus the ~):
96 user_name = src_path + 1;
97 remainder[0] = '\0';
98 }
99 else
100 {
101 int user_name_len = first_slash - src_path - 1;
Greg Claytona5d24f62010-07-01 17:07:48 +0000102 ::memcpy (user_home, src_path + 1, user_name_len);
Jim Inghamf818ca32010-07-01 01:48:53 +0000103 user_home[user_name_len] = '\0';
104 user_name = user_home;
105
Greg Claytona5d24f62010-07-01 17:07:48 +0000106 ::strcpy (remainder, first_slash);
Jim Inghamf818ca32010-07-01 01:48:53 +0000107 }
Greg Claytona5d24f62010-07-01 17:07:48 +0000108
Jim Inghamf818ca32010-07-01 01:48:53 +0000109 if (user_name == NULL)
110 return 0;
111 // User name of "" means the current user...
112
113 struct passwd *user_entry;
Greg Claytonc982c762010-07-09 20:39:50 +0000114 const char *home_dir = NULL;
Jim Inghamf818ca32010-07-01 01:48:53 +0000115
116 if (user_name[0] == '\0')
117 {
118 home_dir = GetCachedGlobTildeSlash();
119 }
120 else
121 {
Greg Claytona5d24f62010-07-01 17:07:48 +0000122 user_entry = ::getpwnam (user_name);
Jim Inghamf818ca32010-07-01 01:48:53 +0000123 if (user_entry != NULL)
124 home_dir = user_entry->pw_dir;
125 }
126
127 if (home_dir == NULL)
128 return 0;
129 else
130 return ::snprintf (dst_path, dst_len, "%s%s", home_dir, remainder);
131}
132
Greg Claytonc982c762010-07-09 20:39:50 +0000133size_t
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000134FileSpec::Resolve (const char *src_path, char *dst_path, size_t dst_len)
135{
136 if (src_path == NULL || src_path[0] == '\0')
137 return 0;
138
139 // Glob if needed for ~/, otherwise copy in case src_path is same as dst_path...
140 char unglobbed_path[PATH_MAX];
Jim Inghamf818ca32010-07-01 01:48:53 +0000141 if (src_path[0] == '~')
142 {
Greg Claytonc982c762010-07-09 20:39:50 +0000143 size_t return_count = ResolveUsername(src_path, unglobbed_path, sizeof(unglobbed_path));
Jim Inghamf818ca32010-07-01 01:48:53 +0000144
145 // If we couldn't find the user referred to, or the resultant path was too long,
146 // then just copy over the src_path.
147 if (return_count == 0 || return_count >= sizeof(unglobbed_path))
148 ::snprintf (unglobbed_path, sizeof(unglobbed_path), "%s", src_path);
149 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000150 else
151 ::snprintf(unglobbed_path, sizeof(unglobbed_path), "%s", src_path);
152
153 // Now resolve the path if needed
154 char resolved_path[PATH_MAX];
155 if (::realpath (unglobbed_path, resolved_path))
156 {
157 // Success, copy the resolved path
158 return ::snprintf(dst_path, dst_len, "%s", resolved_path);
159 }
160 else
161 {
162 // Failed, just copy the unglobbed path
163 return ::snprintf(dst_path, dst_len, "%s", unglobbed_path);
164 }
165}
166
167FileSpec::FileSpec() :
168 m_directory(),
169 m_filename()
170{
171}
172
173//------------------------------------------------------------------
174// Default constructor that can take an optional full path to a
175// file on disk.
176//------------------------------------------------------------------
Jim Ingham0909e5f2010-09-16 00:57:33 +0000177FileSpec::FileSpec(const char *pathname, bool resolve_path) :
178 m_directory(),
Greg Clayton7481c202010-11-08 00:28:40 +0000179 m_filename(),
180 m_is_resolved(false)
Jim Ingham0909e5f2010-09-16 00:57:33 +0000181{
182 if (pathname && pathname[0])
183 SetFile(pathname, resolve_path);
184}
185
186//------------------------------------------------------------------
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000187// Copy constructor
188//------------------------------------------------------------------
189FileSpec::FileSpec(const FileSpec& rhs) :
190 m_directory (rhs.m_directory),
Greg Clayton7481c202010-11-08 00:28:40 +0000191 m_filename (rhs.m_filename),
192 m_is_resolved (rhs.m_is_resolved)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000193{
194}
195
196//------------------------------------------------------------------
197// Copy constructor
198//------------------------------------------------------------------
199FileSpec::FileSpec(const FileSpec* rhs) :
200 m_directory(),
201 m_filename()
202{
203 if (rhs)
204 *this = *rhs;
205}
206
207//------------------------------------------------------------------
208// Virtual destrcuctor in case anyone inherits from this class.
209//------------------------------------------------------------------
210FileSpec::~FileSpec()
211{
212}
213
214//------------------------------------------------------------------
215// Assignment operator.
216//------------------------------------------------------------------
217const FileSpec&
218FileSpec::operator= (const FileSpec& rhs)
219{
220 if (this != &rhs)
221 {
222 m_directory = rhs.m_directory;
223 m_filename = rhs.m_filename;
Greg Clayton7481c202010-11-08 00:28:40 +0000224 m_is_resolved = rhs.m_is_resolved;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000225 }
226 return *this;
227}
228
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000229//------------------------------------------------------------------
230// Update the contents of this object with a new path. The path will
231// be split up into a directory and filename and stored as uniqued
232// string values for quick comparison and efficient memory usage.
233//------------------------------------------------------------------
234void
Greg Clayton7481c202010-11-08 00:28:40 +0000235FileSpec::SetFile (const char *pathname, bool resolve)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000236{
237 m_filename.Clear();
238 m_directory.Clear();
Greg Clayton7481c202010-11-08 00:28:40 +0000239 m_is_resolved = false;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000240 if (pathname == NULL || pathname[0] == '\0')
241 return;
242
243 char resolved_path[PATH_MAX];
Jim Ingham0909e5f2010-09-16 00:57:33 +0000244 bool path_fit = true;
245
246 if (resolve)
247 {
248 path_fit = (FileSpec::Resolve (pathname, resolved_path, sizeof(resolved_path)) < sizeof(resolved_path) - 1);
Greg Clayton7481c202010-11-08 00:28:40 +0000249 m_is_resolved = path_fit;
Jim Ingham0909e5f2010-09-16 00:57:33 +0000250 }
251 else
252 {
Greg Clayton7481c202010-11-08 00:28:40 +0000253 // Copy the path because "basename" and "dirname" want to muck with the
254 // path buffer
255 if (::strlen (pathname) > sizeof(resolved_path) - 1)
Jim Ingham0909e5f2010-09-16 00:57:33 +0000256 path_fit = false;
257 else
Greg Clayton7481c202010-11-08 00:28:40 +0000258 ::strcpy (resolved_path, pathname);
Jim Ingham0909e5f2010-09-16 00:57:33 +0000259 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000260
Jim Ingham0909e5f2010-09-16 00:57:33 +0000261
262 if (path_fit)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000263 {
264 char *filename = ::basename (resolved_path);
265 if (filename)
266 {
267 m_filename.SetCString (filename);
268 // Truncate the basename off the end of the resolved path
269
270 // Only attempt to get the dirname if it looks like we have a path
271 if (strchr(resolved_path, '/'))
272 {
273 char *directory = ::dirname (resolved_path);
274
275 // Make sure we didn't get our directory resolved to "." without having
276 // specified
277 if (directory)
278 m_directory.SetCString(directory);
279 else
280 {
281 char *last_resolved_path_slash = strrchr(resolved_path, '/');
282 if (last_resolved_path_slash)
283 {
284 *last_resolved_path_slash = '\0';
285 m_directory.SetCString(resolved_path);
286 }
287 }
288 }
289 }
290 else
291 m_directory.SetCString(resolved_path);
292 }
293}
294
295//----------------------------------------------------------------------
296// Convert to pointer operator. This allows code to check any FileSpec
297// objects to see if they contain anything valid using code such as:
298//
299// if (file_spec)
300// {}
301//----------------------------------------------------------------------
302FileSpec::operator
303void*() const
304{
305 return (m_directory || m_filename) ? const_cast<FileSpec*>(this) : NULL;
306}
307
308//----------------------------------------------------------------------
309// Logical NOT operator. This allows code to check any FileSpec
310// objects to see if they are invalid using code such as:
311//
312// if (!file_spec)
313// {}
314//----------------------------------------------------------------------
315bool
316FileSpec::operator!() const
317{
318 return !m_directory && !m_filename;
319}
320
321//------------------------------------------------------------------
322// Equal to operator
323//------------------------------------------------------------------
324bool
325FileSpec::operator== (const FileSpec& rhs) const
326{
Greg Clayton7481c202010-11-08 00:28:40 +0000327 if (m_filename == rhs.m_filename)
328 {
329 if (m_directory == rhs.m_directory)
330 return true;
331
332 // TODO: determine if we want to keep this code in here.
333 // The code below was added to handle a case where we were
334 // trying to set a file and line breakpoint and one path
335 // was resolved, and the other not and the directory was
336 // in a mount point that resolved to a more complete path:
337 // "/tmp/a.c" == "/private/tmp/a.c". I might end up pulling
338 // this out...
339 if (IsResolved() && rhs.IsResolved())
340 {
341 // Both paths are resolved, no need to look further...
342 return false;
343 }
344
345 FileSpec resolved_lhs(*this);
346
347 // If "this" isn't resolved, resolve it
348 if (!IsResolved())
349 {
350 if (resolved_lhs.ResolvePath())
351 {
352 // This path wasn't resolved but now it is. Check if the resolved
353 // directory is the same as our unresolved directory, and if so,
354 // we can mark this object as resolved to avoid more future resolves
355 m_is_resolved = (m_directory == resolved_lhs.m_directory);
356 }
357 else
358 return false;
359 }
360
361 FileSpec resolved_rhs(rhs);
362 if (!rhs.IsResolved())
363 {
364 if (resolved_rhs.ResolvePath())
365 {
366 // rhs's path wasn't resolved but now it is. Check if the resolved
367 // directory is the same as rhs's unresolved directory, and if so,
368 // we can mark this object as resolved to avoid more future resolves
369 rhs.m_is_resolved = (m_directory == resolved_rhs.m_directory);
370 }
371 else
372 return false;
373 }
374
375 // If we reach this point in the code we were able to resolve both paths
376 // and since we only resolve the paths if the basenames are equal, then
377 // we can just check if both directories are equal...
378 return resolved_lhs.GetDirectory() == resolved_rhs.GetDirectory();
379 }
380 return false;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000381}
382
383//------------------------------------------------------------------
384// Not equal to operator
385//------------------------------------------------------------------
386bool
387FileSpec::operator!= (const FileSpec& rhs) const
388{
Greg Clayton7481c202010-11-08 00:28:40 +0000389 return !(*this == rhs);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000390}
391
392//------------------------------------------------------------------
393// Less than operator
394//------------------------------------------------------------------
395bool
396FileSpec::operator< (const FileSpec& rhs) const
397{
398 return FileSpec::Compare(*this, rhs, true) < 0;
399}
400
401//------------------------------------------------------------------
402// Dump a FileSpec object to a stream
403//------------------------------------------------------------------
404Stream&
405lldb_private::operator << (Stream &s, const FileSpec& f)
406{
407 f.Dump(&s);
408 return s;
409}
410
411//------------------------------------------------------------------
412// Clear this object by releasing both the directory and filename
413// string values and making them both the empty string.
414//------------------------------------------------------------------
415void
416FileSpec::Clear()
417{
418 m_directory.Clear();
419 m_filename.Clear();
420}
421
422//------------------------------------------------------------------
423// Compare two FileSpec objects. If "full" is true, then both
424// the directory and the filename must match. If "full" is false,
425// then the directory names for "a" and "b" are only compared if
426// they are both non-empty. This allows a FileSpec object to only
427// contain a filename and it can match FileSpec objects that have
428// matching filenames with different paths.
429//
430// Return -1 if the "a" is less than "b", 0 if "a" is equal to "b"
431// and "1" if "a" is greater than "b".
432//------------------------------------------------------------------
433int
434FileSpec::Compare(const FileSpec& a, const FileSpec& b, bool full)
435{
436 int result = 0;
437
438 // If full is true, then we must compare both the directory and filename.
439
440 // If full is false, then if either directory is empty, then we match on
441 // the basename only, and if both directories have valid values, we still
442 // do a full compare. This allows for matching when we just have a filename
443 // in one of the FileSpec objects.
444
445 if (full || (a.m_directory && b.m_directory))
446 {
447 result = ConstString::Compare(a.m_directory, b.m_directory);
448 if (result)
449 return result;
450 }
451 return ConstString::Compare (a.m_filename, b.m_filename);
452}
453
454bool
455FileSpec::Equal (const FileSpec& a, const FileSpec& b, bool full)
456{
457 if (full)
458 return a == b;
459 else
460 return a.m_filename == b.m_filename;
461}
462
463
464
465//------------------------------------------------------------------
466// Dump the object to the supplied stream. If the object contains
467// a valid directory name, it will be displayed followed by a
468// directory delimiter, and the filename.
469//------------------------------------------------------------------
470void
471FileSpec::Dump(Stream *s) const
472{
473 if (m_filename)
474 m_directory.Dump(s, ""); // Provide a default for m_directory when we dump it in case it is invalid
475
476 if (m_directory)
477 {
478 // If dirname was valid, then we need to print a slash between
479 // the directory and the filename
480 s->PutChar('/');
481 }
482 m_filename.Dump(s);
483}
484
485//------------------------------------------------------------------
486// Returns true if the file exists.
487//------------------------------------------------------------------
488bool
489FileSpec::Exists () const
490{
491 struct stat file_stats;
492 return GetFileStats (this, &file_stats);
493}
494
Caroline Tice428a9a52010-09-10 04:48:55 +0000495bool
496FileSpec::ResolveExecutableLocation ()
497{
Greg Clayton274060b2010-10-20 20:54:39 +0000498 if (!m_directory)
Caroline Tice391a9602010-09-12 00:10:52 +0000499 {
Greg Clayton58f41712011-01-25 21:32:01 +0000500 const char *file_cstr = m_filename.GetCString();
501 if (file_cstr)
Caroline Tice391a9602010-09-12 00:10:52 +0000502 {
Greg Clayton58f41712011-01-25 21:32:01 +0000503 const std::string file_str (file_cstr);
504 llvm::sys::Path path = llvm::sys::Program::FindProgramByName (file_str);
505 const std::string &path_str = path.str();
506 llvm::StringRef dir_ref = llvm::sys::path::parent_path(path_str);
507 //llvm::StringRef dir_ref = path.getDirname();
508 if (! dir_ref.empty())
Caroline Tice391a9602010-09-12 00:10:52 +0000509 {
Greg Clayton58f41712011-01-25 21:32:01 +0000510 // FindProgramByName returns "." if it can't find the file.
511 if (strcmp (".", dir_ref.data()) == 0)
512 return false;
513
514 m_directory.SetCString (dir_ref.data());
515 if (Exists())
Caroline Tice391a9602010-09-12 00:10:52 +0000516 return true;
Greg Clayton58f41712011-01-25 21:32:01 +0000517 else
518 {
519 // If FindProgramByName found the file, it returns the directory + filename in its return results.
520 // We need to separate them.
521 FileSpec tmp_file (dir_ref.data(), false);
522 if (tmp_file.Exists())
523 {
524 m_directory = tmp_file.m_directory;
525 return true;
526 }
Caroline Tice391a9602010-09-12 00:10:52 +0000527 }
528 }
529 }
530 }
531
532 return false;
Caroline Tice428a9a52010-09-10 04:48:55 +0000533}
534
Jim Ingham0909e5f2010-09-16 00:57:33 +0000535bool
536FileSpec::ResolvePath ()
537{
Greg Clayton7481c202010-11-08 00:28:40 +0000538 if (m_is_resolved)
539 return true; // We have already resolved this path
540
541 char path_buf[PATH_MAX];
Jim Ingham0909e5f2010-09-16 00:57:33 +0000542 if (!GetPath (path_buf, PATH_MAX))
543 return false;
Greg Clayton7481c202010-11-08 00:28:40 +0000544 // SetFile(...) will set m_is_resolved correctly if it can resolve the path
Jim Ingham0909e5f2010-09-16 00:57:33 +0000545 SetFile (path_buf, true);
Greg Clayton7481c202010-11-08 00:28:40 +0000546 return m_is_resolved;
Jim Ingham0909e5f2010-09-16 00:57:33 +0000547}
548
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000549uint64_t
550FileSpec::GetByteSize() const
551{
552 struct stat file_stats;
553 if (GetFileStats (this, &file_stats))
554 return file_stats.st_size;
555 return 0;
556}
557
558FileSpec::FileType
559FileSpec::GetFileType () const
560{
561 struct stat file_stats;
562 if (GetFileStats (this, &file_stats))
563 {
564 mode_t file_type = file_stats.st_mode & S_IFMT;
565 switch (file_type)
566 {
567 case S_IFDIR: return eFileTypeDirectory;
568 case S_IFIFO: return eFileTypePipe;
569 case S_IFREG: return eFileTypeRegular;
570 case S_IFSOCK: return eFileTypeSocket;
571 case S_IFLNK: return eFileTypeSymbolicLink;
572 default:
573 break;
574 }
575 return eFileTypeUknown;
576 }
577 return eFileTypeInvalid;
578}
579
580TimeValue
581FileSpec::GetModificationTime () const
582{
583 TimeValue mod_time;
584 struct stat file_stats;
585 if (GetFileStats (this, &file_stats))
Eli Friedman6abb6342010-06-11 04:52:22 +0000586 mod_time.OffsetWithSeconds(file_stats.st_mtime);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000587 return mod_time;
588}
589
590//------------------------------------------------------------------
591// Directory string get accessor.
592//------------------------------------------------------------------
593ConstString &
594FileSpec::GetDirectory()
595{
596 return m_directory;
597}
598
599//------------------------------------------------------------------
600// Directory string const get accessor.
601//------------------------------------------------------------------
602const ConstString &
603FileSpec::GetDirectory() const
604{
605 return m_directory;
606}
607
608//------------------------------------------------------------------
609// Filename string get accessor.
610//------------------------------------------------------------------
611ConstString &
612FileSpec::GetFilename()
613{
614 return m_filename;
615}
616
617//------------------------------------------------------------------
618// Filename string const get accessor.
619//------------------------------------------------------------------
620const ConstString &
621FileSpec::GetFilename() const
622{
623 return m_filename;
624}
625
626//------------------------------------------------------------------
627// Extract the directory and path into a fixed buffer. This is
628// needed as the directory and path are stored in separate string
629// values.
630//------------------------------------------------------------------
Greg Claytoncfd1ace2010-10-31 03:01:06 +0000631size_t
632FileSpec::GetPath(char *path, size_t path_max_len) const
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000633{
Greg Claytoncfd1ace2010-10-31 03:01:06 +0000634 if (path_max_len)
Greg Claytondd36def2010-10-17 22:03:32 +0000635 {
Greg Claytoncfd1ace2010-10-31 03:01:06 +0000636 const char *dirname = m_directory.GetCString();
637 const char *filename = m_filename.GetCString();
Greg Claytondd36def2010-10-17 22:03:32 +0000638 if (dirname)
639 {
640 if (filename)
Greg Claytoncfd1ace2010-10-31 03:01:06 +0000641 return ::snprintf (path, path_max_len, "%s/%s", dirname, filename);
Greg Claytondd36def2010-10-17 22:03:32 +0000642 else
Greg Claytoncfd1ace2010-10-31 03:01:06 +0000643 return ::snprintf (path, path_max_len, "%s", dirname);
Greg Claytondd36def2010-10-17 22:03:32 +0000644 }
645 else if (filename)
646 {
Greg Claytoncfd1ace2010-10-31 03:01:06 +0000647 return ::snprintf (path, path_max_len, "%s", filename);
Greg Claytondd36def2010-10-17 22:03:32 +0000648 }
649 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000650 path[0] = '\0';
Greg Claytoncfd1ace2010-10-31 03:01:06 +0000651 return 0;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000652}
653
654//------------------------------------------------------------------
655// Returns a shared pointer to a data buffer that contains all or
656// part of the contents of a file. The data is memory mapped and
657// will lazily page in data from the file as memory is accessed.
658// The data that is mappped will start "file_offset" bytes into the
659// file, and "file_size" bytes will be mapped. If "file_size" is
660// greater than the number of bytes available in the file starting
661// at "file_offset", the number of bytes will be appropriately
662// truncated. The final number of bytes that get mapped can be
663// verified using the DataBuffer::GetByteSize() function.
664//------------------------------------------------------------------
665DataBufferSP
666FileSpec::MemoryMapFileContents(off_t file_offset, size_t file_size) const
667{
668 DataBufferSP data_sp;
669 auto_ptr<DataBufferMemoryMap> mmap_data(new DataBufferMemoryMap());
670 if (mmap_data.get())
671 {
672 if (mmap_data->MemoryMapFromFileSpec (this, file_offset, file_size) >= file_size)
673 data_sp.reset(mmap_data.release());
674 }
675 return data_sp;
676}
677
678
679//------------------------------------------------------------------
680// Return the size in bytes that this object takes in memory. This
681// returns the size in bytes of this object, not any shared string
682// values it may refer to.
683//------------------------------------------------------------------
684size_t
685FileSpec::MemorySize() const
686{
687 return m_filename.MemorySize() + m_directory.MemorySize();
688}
689
Greg Claytondda4f7b2010-06-30 23:03:03 +0000690
691size_t
692FileSpec::ReadFileContents (off_t file_offset, void *dst, size_t dst_len) const
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000693{
Greg Claytondda4f7b2010-06-30 23:03:03 +0000694 size_t bytes_read = 0;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000695 char resolved_path[PATH_MAX];
696 if (GetPath(resolved_path, sizeof(resolved_path)))
697 {
698 int fd = ::open (resolved_path, O_RDONLY, 0);
699 if (fd != -1)
700 {
701 struct stat file_stats;
702 if (::fstat (fd, &file_stats) == 0)
703 {
704 // Read bytes directly into our basic_string buffer
705 if (file_stats.st_size > 0)
706 {
707 off_t lseek_result = 0;
708 if (file_offset > 0)
709 lseek_result = ::lseek (fd, file_offset, SEEK_SET);
710
Greg Claytondda4f7b2010-06-30 23:03:03 +0000711 if (lseek_result == file_offset)
712 {
713 ssize_t n = ::read (fd, dst, dst_len);
714 if (n >= 0)
715 bytes_read = n;
716 }
717 }
718 }
719 }
720 close(fd);
721 }
722 return bytes_read;
723}
724
725//------------------------------------------------------------------
726// Returns a shared pointer to a data buffer that contains all or
727// part of the contents of a file. The data copies into a heap based
728// buffer that lives in the DataBuffer shared pointer object returned.
729// The data that is cached will start "file_offset" bytes into the
730// file, and "file_size" bytes will be mapped. If "file_size" is
731// greater than the number of bytes available in the file starting
732// at "file_offset", the number of bytes will be appropriately
733// truncated. The final number of bytes that get mapped can be
734// verified using the DataBuffer::GetByteSize() function.
735//------------------------------------------------------------------
736DataBufferSP
737FileSpec::ReadFileContents (off_t file_offset, size_t file_size) const
738{
739 DataBufferSP data_sp;
740 char resolved_path[PATH_MAX];
741 if (GetPath(resolved_path, sizeof(resolved_path)))
742 {
743 int fd = ::open (resolved_path, O_RDONLY, 0);
744 if (fd != -1)
745 {
746 struct stat file_stats;
747 if (::fstat (fd, &file_stats) == 0)
748 {
749 if (file_stats.st_size > 0)
750 {
751 off_t lseek_result = 0;
752 if (file_offset > 0)
753 lseek_result = ::lseek (fd, file_offset, SEEK_SET);
754
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000755 if (lseek_result < 0)
756 {
757 // Get error from errno
758 }
759 else if (lseek_result == file_offset)
760 {
Greg Claytondda4f7b2010-06-30 23:03:03 +0000761 const size_t bytes_left = file_stats.st_size - file_offset;
762 size_t num_bytes_to_read = file_size;
763 if (num_bytes_to_read > bytes_left)
764 num_bytes_to_read = bytes_left;
765
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000766 std::auto_ptr<DataBufferHeap> data_heap_ap;
Greg Claytondda4f7b2010-06-30 23:03:03 +0000767 data_heap_ap.reset(new DataBufferHeap(num_bytes_to_read, '\0'));
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000768
769 if (data_heap_ap.get())
770 {
771 ssize_t bytesRead = ::read (fd, (void *)data_heap_ap->GetBytes(), data_heap_ap->GetByteSize());
772 if (bytesRead >= 0)
773 {
774 // Make sure we read exactly what we asked for and if we got
775 // less, adjust the array
Greg Claytonc982c762010-07-09 20:39:50 +0000776 if ((size_t)bytesRead < data_heap_ap->GetByteSize())
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000777 data_heap_ap->SetByteSize(bytesRead);
778 data_sp.reset(data_heap_ap.release());
779 }
780 }
781 }
782 }
783 }
784 }
785 close(fd);
786 }
787 return data_sp;
788}
789
Greg Clayton58fc50e2010-10-20 22:52:05 +0000790size_t
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000791FileSpec::ReadFileLines (STLStringArray &lines)
792{
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000793 lines.clear();
Greg Clayton58fc50e2010-10-20 22:52:05 +0000794 char path[PATH_MAX];
795 if (GetPath(path, sizeof(path)))
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000796 {
Greg Clayton58fc50e2010-10-20 22:52:05 +0000797 ifstream file_stream (path);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000798
Greg Clayton58fc50e2010-10-20 22:52:05 +0000799 if (file_stream)
800 {
801 std::string line;
802 while (getline (file_stream, line))
803 lines.push_back (line);
804 }
805 }
806 return lines.size();
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000807}