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