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