FileSpec.cpp revision 537a7a86687683fd403ce652d178fbc89e06ef9f
1//===-- 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 <libgen.h>
13#include <stdlib.h>
14#include <sys/param.h>
15#include <sys/stat.h>
16#include <sys/types.h>
17#include <pwd.h>
18
19#include <fstream>
20
21#include "llvm/ADT/StringRef.h"
22#include "llvm/System/Path.h"
23#include "llvm/System/Program.h"
24
25#include "lldb/Core/FileSpec.h"
26#include "lldb/Core/DataBufferHeap.h"
27#include "lldb/Core/DataBufferMemoryMap.h"
28#include "lldb/Core/Stream.h"
29#include "lldb/Host/Host.h"
30
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    {
50        struct passwd *user_entry;
51        user_entry = getpwuid(geteuid());
52        if (user_entry != NULL)
53            g_tilde = user_entry->pw_dir;
54
55        if (g_tilde.empty())
56            return NULL;
57    }
58    return g_tilde.c_str();
59}
60
61// 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...
66size_t
67FileSpec::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;
74
75    // 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    {
78        size_t len = strlen (src_path);
79        if (len >= dst_len)
80        {
81            ::bcopy (src_path, dst_path, dst_len - 1);
82            dst_path[dst_len] = '\0';
83        }
84        else
85            ::bcopy (src_path, dst_path, len + 1);
86
87        return len;
88    }
89
90    const char *first_slash = ::strchr (src_path, '/');
91    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;
102        ::memcpy (user_home, src_path + 1, user_name_len);
103        user_home[user_name_len] = '\0';
104        user_name = user_home;
105
106        ::strcpy (remainder, first_slash);
107    }
108
109    if (user_name == NULL)
110        return 0;
111    // User name of "" means the current user...
112
113    struct passwd *user_entry;
114    const char *home_dir = NULL;
115
116    if (user_name[0] == '\0')
117    {
118        home_dir = GetCachedGlobTildeSlash();
119    }
120    else
121    {
122        user_entry = ::getpwnam (user_name);
123        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
133size_t
134FileSpec::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];
141    if (src_path[0] == '~')
142    {
143        size_t return_count = ResolveUsername(src_path, unglobbed_path, sizeof(unglobbed_path));
144
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    }
150    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//------------------------------------------------------------------
177FileSpec::FileSpec(const char *pathname, bool resolve_path) :
178    m_directory(),
179    m_filename()
180{
181    if (pathname && pathname[0])
182        SetFile(pathname, resolve_path);
183}
184
185//------------------------------------------------------------------
186// Copy constructor
187//------------------------------------------------------------------
188FileSpec::FileSpec(const FileSpec& rhs) :
189    m_directory (rhs.m_directory),
190    m_filename (rhs.m_filename)
191{
192}
193
194//------------------------------------------------------------------
195// Copy constructor
196//------------------------------------------------------------------
197FileSpec::FileSpec(const FileSpec* rhs) :
198    m_directory(),
199    m_filename()
200{
201    if (rhs)
202        *this = *rhs;
203}
204
205//------------------------------------------------------------------
206// Virtual destrcuctor in case anyone inherits from this class.
207//------------------------------------------------------------------
208FileSpec::~FileSpec()
209{
210}
211
212//------------------------------------------------------------------
213// Assignment operator.
214//------------------------------------------------------------------
215const FileSpec&
216FileSpec::operator= (const FileSpec& rhs)
217{
218    if (this != &rhs)
219    {
220        m_directory = rhs.m_directory;
221        m_filename = rhs.m_filename;
222    }
223    return *this;
224}
225
226//------------------------------------------------------------------
227// Update the contents of this object with a new path. The path will
228// be split up into a directory and filename and stored as uniqued
229// string values for quick comparison and efficient memory usage.
230//------------------------------------------------------------------
231void
232FileSpec::SetFile(const char *pathname, bool resolve)
233{
234    m_filename.Clear();
235    m_directory.Clear();
236    if (pathname == NULL || pathname[0] == '\0')
237        return;
238
239    char resolved_path[PATH_MAX];
240    bool path_fit = true;
241
242    if (resolve)
243    {
244        path_fit = (FileSpec::Resolve (pathname, resolved_path, sizeof(resolved_path)) < sizeof(resolved_path) - 1);
245    }
246    else
247    {
248        if (strlen (pathname) > sizeof(resolved_path) - 1)
249            path_fit = false;
250        else
251            strcpy (resolved_path, pathname);
252    }
253
254
255    if (path_fit)
256    {
257        char *filename = ::basename (resolved_path);
258        if (filename)
259        {
260            m_filename.SetCString (filename);
261            // Truncate the basename off the end of the resolved path
262
263            // Only attempt to get the dirname if it looks like we have a path
264            if (strchr(resolved_path, '/'))
265            {
266                char *directory = ::dirname (resolved_path);
267
268                // Make sure we didn't get our directory resolved to "." without having
269                // specified
270                if (directory)
271                    m_directory.SetCString(directory);
272                else
273                {
274                    char *last_resolved_path_slash = strrchr(resolved_path, '/');
275                    if (last_resolved_path_slash)
276                    {
277                        *last_resolved_path_slash = '\0';
278                        m_directory.SetCString(resolved_path);
279                    }
280                }
281            }
282        }
283        else
284            m_directory.SetCString(resolved_path);
285    }
286}
287
288//----------------------------------------------------------------------
289// Convert to pointer operator. This allows code to check any FileSpec
290// objects to see if they contain anything valid using code such as:
291//
292//  if (file_spec)
293//  {}
294//----------------------------------------------------------------------
295FileSpec::operator
296void*() const
297{
298    return (m_directory || m_filename) ? const_cast<FileSpec*>(this) : NULL;
299}
300
301//----------------------------------------------------------------------
302// Logical NOT operator. This allows code to check any FileSpec
303// objects to see if they are invalid using code such as:
304//
305//  if (!file_spec)
306//  {}
307//----------------------------------------------------------------------
308bool
309FileSpec::operator!() const
310{
311    return !m_directory && !m_filename;
312}
313
314//------------------------------------------------------------------
315// Equal to operator
316//------------------------------------------------------------------
317bool
318FileSpec::operator== (const FileSpec& rhs) const
319{
320    return m_directory == rhs.m_directory && m_filename == rhs.m_filename;
321}
322
323//------------------------------------------------------------------
324// Not equal to operator
325//------------------------------------------------------------------
326bool
327FileSpec::operator!= (const FileSpec& rhs) const
328{
329    return m_filename != rhs.m_filename || m_directory != rhs.m_directory;
330}
331
332//------------------------------------------------------------------
333// Less than operator
334//------------------------------------------------------------------
335bool
336FileSpec::operator< (const FileSpec& rhs) const
337{
338    return FileSpec::Compare(*this, rhs, true) < 0;
339}
340
341//------------------------------------------------------------------
342// Dump a FileSpec object to a stream
343//------------------------------------------------------------------
344Stream&
345lldb_private::operator << (Stream &s, const FileSpec& f)
346{
347    f.Dump(&s);
348    return s;
349}
350
351//------------------------------------------------------------------
352// Clear this object by releasing both the directory and filename
353// string values and making them both the empty string.
354//------------------------------------------------------------------
355void
356FileSpec::Clear()
357{
358    m_directory.Clear();
359    m_filename.Clear();
360}
361
362//------------------------------------------------------------------
363// Compare two FileSpec objects. If "full" is true, then both
364// the directory and the filename must match. If "full" is false,
365// then the directory names for "a" and "b" are only compared if
366// they are both non-empty. This allows a FileSpec object to only
367// contain a filename and it can match FileSpec objects that have
368// matching filenames with different paths.
369//
370// Return -1 if the "a" is less than "b", 0 if "a" is equal to "b"
371// and "1" if "a" is greater than "b".
372//------------------------------------------------------------------
373int
374FileSpec::Compare(const FileSpec& a, const FileSpec& b, bool full)
375{
376    int result = 0;
377
378    // If full is true, then we must compare both the directory and filename.
379
380    // If full is false, then if either directory is empty, then we match on
381    // the basename only, and if both directories have valid values, we still
382    // do a full compare. This allows for matching when we just have a filename
383    // in one of the FileSpec objects.
384
385    if (full || (a.m_directory && b.m_directory))
386    {
387        result = ConstString::Compare(a.m_directory, b.m_directory);
388        if (result)
389            return result;
390    }
391    return ConstString::Compare (a.m_filename, b.m_filename);
392}
393
394bool
395FileSpec::Equal (const FileSpec& a, const FileSpec& b, bool full)
396{
397    if (full)
398        return a == b;
399    else
400        return a.m_filename == b.m_filename;
401}
402
403
404
405//------------------------------------------------------------------
406// Dump the object to the supplied stream. If the object contains
407// a valid directory name, it will be displayed followed by a
408// directory delimiter, and the filename.
409//------------------------------------------------------------------
410void
411FileSpec::Dump(Stream *s) const
412{
413    if (m_filename)
414        m_directory.Dump(s, "");    // Provide a default for m_directory when we dump it in case it is invalid
415
416    if (m_directory)
417    {
418        // If dirname was valid, then we need to print a slash between
419        // the directory and the filename
420        s->PutChar('/');
421    }
422    m_filename.Dump(s);
423}
424
425//------------------------------------------------------------------
426// Returns true if the file exists.
427//------------------------------------------------------------------
428bool
429FileSpec::Exists () const
430{
431    struct stat file_stats;
432    return GetFileStats (this, &file_stats);
433}
434
435bool
436FileSpec::ResolveExecutableLocation ()
437{
438    if (!m_directory)
439    {
440        const std::string file_str (m_filename.AsCString());
441        llvm::sys::Path path = llvm::sys::Program::FindProgramByName (file_str);
442        llvm::StringRef dir_ref = path.getDirname();
443        if (! dir_ref.empty())
444        {
445            // FindProgramByName returns "." if it can't find the file.
446            if (strcmp (".", dir_ref.data()) == 0)
447                return false;
448
449            m_directory.SetCString (dir_ref.data());
450            if (Exists())
451                return true;
452            else
453            {
454                // If FindProgramByName found the file, it returns the directory + filename in its return results.
455                // We need to separate them.
456                FileSpec tmp_file (dir_ref.data(), false);
457                if (tmp_file.Exists())
458                {
459                    m_directory = tmp_file.m_directory;
460                    return true;
461                }
462            }
463        }
464    }
465
466    return false;
467}
468
469bool
470FileSpec::ResolvePath ()
471{
472    char path_buf[PATH_MAX];
473
474    if (!GetPath (path_buf, PATH_MAX))
475        return false;
476    SetFile (path_buf, true);
477    return true;
478}
479
480uint64_t
481FileSpec::GetByteSize() const
482{
483    struct stat file_stats;
484    if (GetFileStats (this, &file_stats))
485        return file_stats.st_size;
486    return 0;
487}
488
489FileSpec::FileType
490FileSpec::GetFileType () const
491{
492    struct stat file_stats;
493    if (GetFileStats (this, &file_stats))
494    {
495        mode_t file_type = file_stats.st_mode & S_IFMT;
496        switch (file_type)
497        {
498        case S_IFDIR:   return eFileTypeDirectory;
499        case S_IFIFO:   return eFileTypePipe;
500        case S_IFREG:   return eFileTypeRegular;
501        case S_IFSOCK:  return eFileTypeSocket;
502        case S_IFLNK:   return eFileTypeSymbolicLink;
503        default:
504            break;
505        }
506        return eFileTypeUknown;
507    }
508    return eFileTypeInvalid;
509}
510
511TimeValue
512FileSpec::GetModificationTime () const
513{
514    TimeValue mod_time;
515    struct stat file_stats;
516    if (GetFileStats (this, &file_stats))
517        mod_time.OffsetWithSeconds(file_stats.st_mtime);
518    return mod_time;
519}
520
521//------------------------------------------------------------------
522// Directory string get accessor.
523//------------------------------------------------------------------
524ConstString &
525FileSpec::GetDirectory()
526{
527    return m_directory;
528}
529
530//------------------------------------------------------------------
531// Directory string const get accessor.
532//------------------------------------------------------------------
533const ConstString &
534FileSpec::GetDirectory() const
535{
536    return m_directory;
537}
538
539//------------------------------------------------------------------
540// Filename string get accessor.
541//------------------------------------------------------------------
542ConstString &
543FileSpec::GetFilename()
544{
545    return m_filename;
546}
547
548//------------------------------------------------------------------
549// Filename string const get accessor.
550//------------------------------------------------------------------
551const ConstString &
552FileSpec::GetFilename() const
553{
554    return m_filename;
555}
556
557//------------------------------------------------------------------
558// Extract the directory and path into a fixed buffer. This is
559// needed as the directory and path are stored in separate string
560// values.
561//------------------------------------------------------------------
562bool
563FileSpec::GetPath(char *path, size_t max_path_length) const
564{
565    if (max_path_length)
566    {
567        const char *dirname = m_directory.AsCString();
568        const char *filename = m_filename.AsCString();
569        if (dirname)
570        {
571            if (filename)
572            {
573                return (size_t)::snprintf (path, max_path_length, "%s/%s", dirname, filename) < max_path_length;
574            }
575            else
576            {
577                size_t dir_len = m_directory.GetLength() + 1;
578                if (dir_len < max_path_length)
579                {
580                    ::memcpy (path, dirname, dir_len);
581                    return true;
582                }
583            }
584        }
585        else if (filename)
586        {
587            size_t filename_len = m_filename.GetLength() + 1;
588            if (filename_len < max_path_length)
589            {
590                ::memcpy (path, filename, filename_len);
591                return true;
592            }
593        }
594    }
595    path[0] = '\0';
596    return false;
597}
598
599//------------------------------------------------------------------
600// Returns a shared pointer to a data buffer that contains all or
601// part of the contents of a file. The data is memory mapped and
602// will lazily page in data from the file as memory is accessed.
603// The data that is mappped will start "file_offset" bytes into the
604// file, and "file_size" bytes will be mapped. If "file_size" is
605// greater than the number of bytes available in the file starting
606// at "file_offset", the number of bytes will be appropriately
607// truncated. The final number of bytes that get mapped can be
608// verified using the DataBuffer::GetByteSize() function.
609//------------------------------------------------------------------
610DataBufferSP
611FileSpec::MemoryMapFileContents(off_t file_offset, size_t file_size) const
612{
613    DataBufferSP data_sp;
614    auto_ptr<DataBufferMemoryMap> mmap_data(new DataBufferMemoryMap());
615    if (mmap_data.get())
616    {
617        if (mmap_data->MemoryMapFromFileSpec (this, file_offset, file_size) >= file_size)
618            data_sp.reset(mmap_data.release());
619    }
620    return data_sp;
621}
622
623
624//------------------------------------------------------------------
625// Return the size in bytes that this object takes in memory. This
626// returns the size in bytes of this object, not any shared string
627// values it may refer to.
628//------------------------------------------------------------------
629size_t
630FileSpec::MemorySize() const
631{
632    return m_filename.MemorySize() + m_directory.MemorySize();
633}
634
635
636size_t
637FileSpec::ReadFileContents (off_t file_offset, void *dst, size_t dst_len) const
638{
639    size_t bytes_read = 0;
640    char resolved_path[PATH_MAX];
641    if (GetPath(resolved_path, sizeof(resolved_path)))
642    {
643        int fd = ::open (resolved_path, O_RDONLY, 0);
644        if (fd != -1)
645        {
646            struct stat file_stats;
647            if (::fstat (fd, &file_stats) == 0)
648            {
649                // Read bytes directly into our basic_string buffer
650                if (file_stats.st_size > 0)
651                {
652                    off_t lseek_result = 0;
653                    if (file_offset > 0)
654                        lseek_result = ::lseek (fd, file_offset, SEEK_SET);
655
656                    if (lseek_result == file_offset)
657                    {
658                        ssize_t n = ::read (fd, dst, dst_len);
659                        if (n >= 0)
660                            bytes_read = n;
661                    }
662                }
663            }
664        }
665        close(fd);
666    }
667    return bytes_read;
668}
669
670//------------------------------------------------------------------
671// Returns a shared pointer to a data buffer that contains all or
672// part of the contents of a file. The data copies into a heap based
673// buffer that lives in the DataBuffer shared pointer object returned.
674// The data that is cached will start "file_offset" bytes into the
675// file, and "file_size" bytes will be mapped. If "file_size" is
676// greater than the number of bytes available in the file starting
677// at "file_offset", the number of bytes will be appropriately
678// truncated. The final number of bytes that get mapped can be
679// verified using the DataBuffer::GetByteSize() function.
680//------------------------------------------------------------------
681DataBufferSP
682FileSpec::ReadFileContents (off_t file_offset, size_t file_size) const
683{
684    DataBufferSP data_sp;
685    char resolved_path[PATH_MAX];
686    if (GetPath(resolved_path, sizeof(resolved_path)))
687    {
688        int fd = ::open (resolved_path, O_RDONLY, 0);
689        if (fd != -1)
690        {
691            struct stat file_stats;
692            if (::fstat (fd, &file_stats) == 0)
693            {
694                if (file_stats.st_size > 0)
695                {
696                    off_t lseek_result = 0;
697                    if (file_offset > 0)
698                        lseek_result = ::lseek (fd, file_offset, SEEK_SET);
699
700                    if (lseek_result < 0)
701                    {
702                        // Get error from errno
703                    }
704                    else if (lseek_result == file_offset)
705                    {
706                        const size_t bytes_left = file_stats.st_size - file_offset;
707                        size_t num_bytes_to_read = file_size;
708                        if (num_bytes_to_read > bytes_left)
709                            num_bytes_to_read = bytes_left;
710
711                        std::auto_ptr<DataBufferHeap> data_heap_ap;
712                        data_heap_ap.reset(new DataBufferHeap(num_bytes_to_read, '\0'));
713
714                        if (data_heap_ap.get())
715                        {
716                            ssize_t bytesRead = ::read (fd, (void *)data_heap_ap->GetBytes(), data_heap_ap->GetByteSize());
717                            if (bytesRead >= 0)
718                            {
719                                // Make sure we read exactly what we asked for and if we got
720                                // less, adjust the array
721                                if ((size_t)bytesRead < data_heap_ap->GetByteSize())
722                                    data_heap_ap->SetByteSize(bytesRead);
723                                data_sp.reset(data_heap_ap.release());
724                            }
725                        }
726                    }
727                }
728            }
729        }
730        close(fd);
731    }
732    return data_sp;
733}
734
735bool
736FileSpec::ReadFileLines (STLStringArray &lines)
737{
738    bool ret_val = false;
739    lines.clear();
740
741    std::string dir_str (m_directory.AsCString());
742    std::string file_str (m_filename.AsCString());
743    std::string full_name = dir_str + "/" + file_str;
744
745    ifstream file_stream (full_name.c_str());
746
747    if (file_stream)
748    {
749        std::string line;
750        while (getline (file_stream, line))
751          lines.push_back (line);
752        ret_val = true;
753    }
754
755    return ret_val;
756}
757