FileSpec.cpp revision 27345db7bc4167078014798032137b0452f4cab9
1554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com//===-- FileSpec.cpp --------------------------------------------*- C++ -*-===//
2554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com//
3554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com//                     The LLVM Compiler Infrastructure
4554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com//
5554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com// This file is distributed under the University of Illinois Open Source
6554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com// License. See LICENSE.TXT for details.
7f168b86d7fafc5c20c87bebc6fd393cb17e120catfarina//
8f168b86d7fafc5c20c87bebc6fd393cb17e120catfarina//===----------------------------------------------------------------------===//
9a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com
10554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com
11a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com#include <fcntl.h>
12554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com#include <libgen.h>
13554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com#include <stdlib.h>
14c289743864e2ab926a95e617a5cd1d29b26d1825mtklein@google.com#include <sys/param.h>
15554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com#include <sys/stat.h>
16554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com#include <sys/types.h>
17f168b86d7fafc5c20c87bebc6fd393cb17e120catfarina#include <pwd.h>
18554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com
19644629c1c7913a43ced172b98d56e0f471bc348bcommit-bot@chromium.org#include <fstream>
20644629c1c7913a43ced172b98d56e0f471bc348bcommit-bot@chromium.org
21554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com#include "llvm/ADT/StringRef.h"
22644629c1c7913a43ced172b98d56e0f471bc348bcommit-bot@chromium.org#include "llvm/Support/Path.h"
23554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com#include "llvm/Support/Program.h"
24554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com
25554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com#include "lldb/Core/FileSpec.h"
26554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com#include "lldb/Core/DataBufferHeap.h"
27554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com#include "lldb/Core/DataBufferMemoryMap.h"
283361471a3504ecd0351ff70f4c42d8d6fee963d4commit-bot@chromium.org#include "lldb/Core/Stream.h"
293361471a3504ecd0351ff70f4c42d8d6fee963d4commit-bot@chromium.org#include "lldb/Host/Host.h"
30554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com
31554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.comusing namespace lldb;
32554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.comusing namespace lldb_private;
33554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.comusing namespace std;
34554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com
35554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.comstatic bool
36554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.comGetFileStats (const FileSpec *file_spec, struct stat *stats_ptr)
37554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com{
38554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com    char resolved_path[PATH_MAX];
39f168b86d7fafc5c20c87bebc6fd393cb17e120catfarina    if (file_spec->GetPath(&resolved_path[0], sizeof(resolved_path)))
40554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com        return ::stat (resolved_path, stats_ptr) == 0;
41554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com    return false;
42554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com}
43554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com
4415e9d3e66e161ce23df30bc13f8a0c87d196b463robertphillips@google.comstatic const char*
4515e9d3e66e161ce23df30bc13f8a0c87d196b463robertphillips@google.comGetCachedGlobTildeSlash()
46554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com{
4777472f06f88b85e85fb690584c85d0a42e74b685sugoi@google.com    static std::string g_tilde;
4815e9d3e66e161ce23df30bc13f8a0c87d196b463robertphillips@google.com    if (g_tilde.empty())
4915e9d3e66e161ce23df30bc13f8a0c87d196b463robertphillips@google.com    {
5015e9d3e66e161ce23df30bc13f8a0c87d196b463robertphillips@google.com        struct passwd *user_entry;
51554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com        user_entry = getpwuid(geteuid());
52554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com        if (user_entry != NULL)
53f168b86d7fafc5c20c87bebc6fd393cb17e120catfarina            g_tilde = user_entry->pw_dir;
54554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com
55644629c1c7913a43ced172b98d56e0f471bc348bcommit-bot@chromium.org        if (g_tilde.empty())
56644629c1c7913a43ced172b98d56e0f471bc348bcommit-bot@chromium.org            return NULL;
57554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com    }
58644629c1c7913a43ced172b98d56e0f471bc348bcommit-bot@chromium.org    return g_tilde.c_str();
59554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com}
60554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com
61554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com// Resolves the username part of a path of the form ~user/other/directories, and
62554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com// writes the result into dst_path.
63554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com// Returns 0 if there WAS a ~ in the path but the username couldn't be resolved.
643361471a3504ecd0351ff70f4c42d8d6fee963d4commit-bot@chromium.org// Otherwise returns the number of characters copied into dst_path.  If the return
65554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com// is >= dst_len, then the resolved path is too long...
663361471a3504ecd0351ff70f4c42d8d6fee963d4commit-bot@chromium.orgsize_t
67554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.comFileSpec::ResolveUsername (const char *src_path, char *dst_path, size_t dst_len)
68554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com{
69554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com    char user_home[PATH_MAX];
70554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com    const char *user_name;
71554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com
72554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com    if (src_path == NULL || src_path[0] == '\0')
73554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com        return 0;
74554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com
75554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com    // If there's no ~, then just copy src_path straight to dst_path (they may be the same string...)
76554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com    if (src_path[0] != '~')
77f168b86d7fafc5c20c87bebc6fd393cb17e120catfarina    {
78554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com        size_t len = strlen (src_path);
79554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com        if (len >= dst_len)
80f168b86d7fafc5c20c87bebc6fd393cb17e120catfarina        {
81a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com            ::bcopy (src_path, dst_path, dst_len - 1);
82644629c1c7913a43ced172b98d56e0f471bc348bcommit-bot@chromium.org            dst_path[dst_len] = '\0';
83644629c1c7913a43ced172b98d56e0f471bc348bcommit-bot@chromium.org        }
84a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        else
85644629c1c7913a43ced172b98d56e0f471bc348bcommit-bot@chromium.org            ::bcopy (src_path, dst_path, len + 1);
86a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com
87a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        return len;
88a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    }
89a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com
90a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    const char *first_slash = ::strchr (src_path, '/');
913361471a3504ecd0351ff70f4c42d8d6fee963d4commit-bot@chromium.org    char remainder[PATH_MAX];
923361471a3504ecd0351ff70f4c42d8d6fee963d4commit-bot@chromium.org
93a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    if (first_slash == NULL)
94a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    {
95a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        // The whole name is the username (minus the ~):
96a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        user_name = src_path + 1;
97a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        remainder[0] = '\0';
98a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    }
99a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    else
100a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    {
101a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        int user_name_len = first_slash - src_path - 1;
102a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        ::memcpy (user_home, src_path + 1, user_name_len);
103f168b86d7fafc5c20c87bebc6fd393cb17e120catfarina        user_home[user_name_len] = '\0';
104a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        user_name = user_home;
105a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com
106a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        ::strcpy (remainder, first_slash);
107a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    }
108f168b86d7fafc5c20c87bebc6fd393cb17e120catfarina
109a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    if (user_name == NULL)
110644629c1c7913a43ced172b98d56e0f471bc348bcommit-bot@chromium.org        return 0;
111644629c1c7913a43ced172b98d56e0f471bc348bcommit-bot@chromium.org    // User name of "" means the current user...
112a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com
113644629c1c7913a43ced172b98d56e0f471bc348bcommit-bot@chromium.org    struct passwd *user_entry;
114a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    const char *home_dir = NULL;
115a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com
116a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    if (user_name[0] == '\0')
117a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    {
118a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        home_dir = GetCachedGlobTildeSlash();
1193361471a3504ecd0351ff70f4c42d8d6fee963d4commit-bot@chromium.org    }
1203361471a3504ecd0351ff70f4c42d8d6fee963d4commit-bot@chromium.org    else
121a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    {
122a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        user_entry = ::getpwnam (user_name);
123a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        if (user_entry != NULL)
124a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com            home_dir = user_entry->pw_dir;
125a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    }
126a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com
127a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    if (home_dir == NULL)
128a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        return 0;
129a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    else
130f168b86d7fafc5c20c87bebc6fd393cb17e120catfarina        return ::snprintf (dst_path, dst_len, "%s%s", home_dir, remainder);
131a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com}
132a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com
133a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.comsize_t
134a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.comFileSpec::Resolve (const char *src_path, char *dst_path, size_t dst_len)
135a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com{
13677472f06f88b85e85fb690584c85d0a42e74b685sugoi@google.com    if (src_path == NULL || src_path[0] == '\0')
137a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        return 0;
138a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com
139f168b86d7fafc5c20c87bebc6fd393cb17e120catfarina    // Glob if needed for ~/, otherwise copy in case src_path is same as dst_path...
140a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    char unglobbed_path[PATH_MAX];
141644629c1c7913a43ced172b98d56e0f471bc348bcommit-bot@chromium.org    if (src_path[0] == '~')
142644629c1c7913a43ced172b98d56e0f471bc348bcommit-bot@chromium.org    {
143a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        size_t return_count = ResolveUsername(src_path, unglobbed_path, sizeof(unglobbed_path));
144644629c1c7913a43ced172b98d56e0f471bc348bcommit-bot@chromium.org
145a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        // If we couldn't find the user referred to, or the resultant path was too long,
146a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        // then just copy over the src_path.
147a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        if (return_count == 0 || return_count >= sizeof(unglobbed_path))
148a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com            ::snprintf (unglobbed_path, sizeof(unglobbed_path), "%s", src_path);
149a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    }
1503361471a3504ecd0351ff70f4c42d8d6fee963d4commit-bot@chromium.org    else
151a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        ::snprintf(unglobbed_path, sizeof(unglobbed_path), "%s", src_path);
1523361471a3504ecd0351ff70f4c42d8d6fee963d4commit-bot@chromium.org
153a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    // Now resolve the path if needed
154a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    char resolved_path[PATH_MAX];
155a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    if (::realpath (unglobbed_path, resolved_path))
156a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    {
157a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        // Success, copy the resolved path
158a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        return ::snprintf(dst_path, dst_len, "%s", resolved_path);
159a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    }
160a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    else
161a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    {
162a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        // Failed, just copy the unglobbed path
163f168b86d7fafc5c20c87bebc6fd393cb17e120catfarina        return ::snprintf(dst_path, dst_len, "%s", unglobbed_path);
164a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    }
165a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com}
166f168b86d7fafc5c20c87bebc6fd393cb17e120catfarina
167a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.comFileSpec::FileSpec() :
168644629c1c7913a43ced172b98d56e0f471bc348bcommit-bot@chromium.org    m_directory(),
169644629c1c7913a43ced172b98d56e0f471bc348bcommit-bot@chromium.org    m_filename()
170a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com{
171644629c1c7913a43ced172b98d56e0f471bc348bcommit-bot@chromium.org}
172a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com
173a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com//------------------------------------------------------------------
174a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com// Default constructor that can take an optional full path to a
175a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com// file on disk.
176a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com//------------------------------------------------------------------
1773361471a3504ecd0351ff70f4c42d8d6fee963d4commit-bot@chromium.orgFileSpec::FileSpec(const char *pathname, bool resolve_path) :
1783361471a3504ecd0351ff70f4c42d8d6fee963d4commit-bot@chromium.org    m_directory(),
179a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    m_filename(),
180a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    m_is_resolved(false)
181a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com{
182a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    if (pathname && pathname[0])
183a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com        SetFile(pathname, resolve_path);
184a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com}
185a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com
186a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com//------------------------------------------------------------------
187a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com// Copy constructor
188a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com//------------------------------------------------------------------
189f168b86d7fafc5c20c87bebc6fd393cb17e120catfarinaFileSpec::FileSpec(const FileSpec& rhs) :
190a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    m_directory (rhs.m_directory),
191a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com    m_filename (rhs.m_filename),
192554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com    m_is_resolved (rhs.m_is_resolved)
193554875210043b34178f7ed6ac5bd682b1fad367bbungeman@google.com{
194410e6e80f00a6c660675c80904807a041c7b7d2amtklein@google.com}
195410e6e80f00a6c660675c80904807a041c7b7d2amtklein@google.com
196410e6e80f00a6c660675c80904807a041c7b7d2amtklein@google.com//------------------------------------------------------------------
197a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com// Copy constructor
198410e6e80f00a6c660675c80904807a041c7b7d2amtklein@google.com//------------------------------------------------------------------
199410e6e80f00a6c660675c80904807a041c7b7d2amtklein@google.comFileSpec::FileSpec(const FileSpec* rhs) :
200410e6e80f00a6c660675c80904807a041c7b7d2amtklein@google.com    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;
224        m_is_resolved = rhs.m_is_resolved;
225    }
226    return *this;
227}
228
229//------------------------------------------------------------------
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
235FileSpec::SetFile (const char *pathname, bool resolve)
236{
237    m_filename.Clear();
238    m_directory.Clear();
239    m_is_resolved = false;
240    if (pathname == NULL || pathname[0] == '\0')
241        return;
242
243    char resolved_path[PATH_MAX];
244    bool path_fit = true;
245
246    if (resolve)
247    {
248        path_fit = (FileSpec::Resolve (pathname, resolved_path, sizeof(resolved_path)) < sizeof(resolved_path) - 1);
249        m_is_resolved = path_fit;
250    }
251    else
252    {
253        // Copy the path because "basename" and "dirname" want to muck with the
254        // path buffer
255        if (::strlen (pathname) > sizeof(resolved_path) - 1)
256            path_fit = false;
257        else
258            ::strcpy (resolved_path, pathname);
259    }
260
261
262    if (path_fit)
263    {
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{
327    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;
381}
382
383//------------------------------------------------------------------
384// Not equal to operator
385//------------------------------------------------------------------
386bool
387FileSpec::operator!= (const FileSpec& rhs) const
388{
389    return !(*this == rhs);
390}
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
495bool
496FileSpec::ResolveExecutableLocation ()
497{
498    if (!m_directory)
499    {
500        const char *file_cstr = m_filename.GetCString();
501        if (file_cstr)
502        {
503            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())
509            {
510                // 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())
516                    return true;
517                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                    }
527                }
528            }
529        }
530    }
531
532    return false;
533}
534
535bool
536FileSpec::ResolvePath ()
537{
538    if (m_is_resolved)
539        return true;    // We have already resolved this path
540
541    char path_buf[PATH_MAX];
542    if (!GetPath (path_buf, PATH_MAX))
543        return false;
544    // SetFile(...) will set m_is_resolved correctly if it can resolve the path
545    SetFile (path_buf, true);
546    return m_is_resolved;
547}
548
549uint64_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))
586        mod_time.OffsetWithSeconds(file_stats.st_mtime);
587    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//------------------------------------------------------------------
631size_t
632FileSpec::GetPath(char *path, size_t path_max_len) const
633{
634    if (path_max_len)
635    {
636        const char *dirname = m_directory.GetCString();
637        const char *filename = m_filename.GetCString();
638        if (dirname)
639        {
640            if (filename)
641                return ::snprintf (path, path_max_len, "%s/%s", dirname, filename);
642            else
643                return ::snprintf (path, path_max_len, "%s", dirname);
644        }
645        else if (filename)
646        {
647            return ::snprintf (path, path_max_len, "%s", filename);
648        }
649    }
650    path[0] = '\0';
651    return 0;
652}
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
690
691size_t
692FileSpec::ReadFileContents (off_t file_offset, void *dst, size_t dst_len) const
693{
694    size_t bytes_read = 0;
695    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
711                    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
755                    if (lseek_result < 0)
756                    {
757                        // Get error from errno
758                    }
759                    else if (lseek_result == file_offset)
760                    {
761                        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
766                        std::auto_ptr<DataBufferHeap> data_heap_ap;
767                        data_heap_ap.reset(new DataBufferHeap(num_bytes_to_read, '\0'));
768
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
776                                if ((size_t)bytesRead < data_heap_ap->GetByteSize())
777                                    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
790size_t
791FileSpec::ReadFileLines (STLStringArray &lines)
792{
793    lines.clear();
794    char path[PATH_MAX];
795    if (GetPath(path, sizeof(path)))
796    {
797        ifstream file_stream (path);
798
799        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();
807}
808