1//===- Directory.cpp ------------------------------------------------------===//
2//
3//                     The MCLinker Project
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9#include "mcld/Support/Directory.h"
10#include "mcld/Support/FileSystem.h"
11
12namespace mcld {
13namespace sys {
14namespace fs {
15
16namespace {  // anonymous
17
18bool status_known(FileStatus f) {
19  return f.type() != StatusError;
20}
21
22bool is_symlink(FileStatus f) {
23  return f.type() == SymlinkFile;
24}
25
26const Path dot_path(".");
27const Path dot_dot_path("..");
28
29}  // anonymous namespace
30
31//===----------------------------------------------------------------------===//
32// Directory
33//===----------------------------------------------------------------------===//
34Directory::Directory()
35    : m_Path(),
36      m_FileStatus(),
37      m_SymLinkStatus(),
38      m_Handler(0),
39      m_Cache(),
40      m_CacheFull(false) {
41}
42
43Directory::Directory(const Path& pPath, FileStatus st, FileStatus symlink_st)
44    : m_Path(pPath),
45      m_FileStatus(st),
46      m_SymLinkStatus(symlink_st),
47      m_Handler(0),
48      m_Cache(),
49      m_CacheFull(false) {
50  if (m_Path == dot_path)
51    detail::get_pwd(m_Path);
52  m_Path.m_append_separator_if_needed();
53  detail::open_dir(*this);
54}
55
56Directory::Directory(const char* pPath, FileStatus st, FileStatus symlink_st)
57    : Directory(sys::fs::Path(pPath), st, symlink_st) {
58}
59
60Directory::Directory(const Directory& pCopy)
61    : m_Path(pCopy.m_Path),
62      m_FileStatus(pCopy.m_FileStatus),
63      m_SymLinkStatus(pCopy.m_SymLinkStatus),
64      m_Handler(0),
65      m_Cache(),
66      m_CacheFull(false) {
67  detail::open_dir(*this);
68}
69
70Directory::~Directory() {
71  detail::close_dir(*this);
72}
73
74bool Directory::isGood() const {
75  return (0 != m_Handler);
76}
77
78Directory& Directory::operator=(const Directory& pCopy) {
79  assign(pCopy.m_Path, pCopy.m_FileStatus, pCopy.m_SymLinkStatus);
80  return *this;
81}
82
83void Directory::assign(const Path& pPath,
84                       FileStatus st,
85                       FileStatus symlink_st) {
86  if (isGood())
87    clear();
88
89  m_Path = pPath;
90  if (m_Path == dot_path)
91    detail::get_pwd(m_Path);
92  m_Path.m_append_separator_if_needed();
93
94  m_FileStatus = st;
95  m_SymLinkStatus = symlink_st;
96  detail::open_dir(*this);
97}
98
99FileStatus Directory::status() const {
100  if (!status_known(m_FileStatus)) {
101    // optimization: if the symlink status is known, and it isn't a symlink,
102    // then status and symlink_status are identical so just copy the
103    // symlink status to the regular status.
104    if (status_known(m_SymLinkStatus) && !is_symlink(m_SymLinkStatus)) {
105      m_FileStatus = m_SymLinkStatus;
106    } else
107      detail::status(m_Path, m_FileStatus);
108  }
109  return m_FileStatus;
110}
111
112FileStatus Directory::symlinkStatus() const {
113  if (!status_known(m_SymLinkStatus))
114    detail::symlink_status(m_Path, m_SymLinkStatus);
115  return m_SymLinkStatus;
116}
117
118Directory::iterator Directory::begin() {
119  if (m_CacheFull && m_Cache.empty())
120    return end();
121  PathCache::iterator iter = m_Cache.begin();
122  if (iter.getEntry() == NULL)
123    ++iter;
124  return iterator(this, iter);
125}
126
127Directory::iterator Directory::end() {
128  return iterator(0, m_Cache.end());
129}
130
131void Directory::clear() {
132  m_Path.native().clear();
133  m_FileStatus = FileStatus();
134  m_SymLinkStatus = FileStatus();
135  m_Cache.clear();
136  detail::close_dir(*this);
137}
138
139//==========================
140// DirIterator
141DirIterator::DirIterator(Directory* pParent,
142                         const DirIterator::DirCache::iterator& pIter)
143    : m_pParent(pParent), m_Iter(pIter) {
144  m_pEntry = m_Iter.getEntry();
145}
146
147DirIterator::DirIterator(const DirIterator& pCopy)
148    : m_pParent(pCopy.m_pParent),
149      m_Iter(pCopy.m_Iter),
150      m_pEntry(pCopy.m_pEntry) {
151}
152
153DirIterator::~DirIterator() {
154}
155
156Path* DirIterator::path() {
157  if (m_pParent == NULL)
158    return NULL;
159  return &m_pEntry->value();
160}
161
162const Path* DirIterator::path() const {
163  if (m_pParent == NULL)
164    return NULL;
165  return &m_pEntry->value();
166}
167
168DirIterator& DirIterator::operator=(const DirIterator& pCopy) {
169  m_pParent = pCopy.m_pParent;
170  m_Iter = pCopy.m_Iter;
171  m_pEntry = pCopy.m_pEntry;
172  return (*this);
173}
174
175DirIterator& DirIterator::operator++() {
176  if (m_pParent == 0)
177    return *this;
178
179  // move forward one step first.
180  ++m_Iter;
181
182  if (m_pParent->m_Cache.end() == m_Iter) {
183    if (!m_pParent->m_CacheFull) {
184      m_pEntry = detail::bring_one_into_cache(*this);
185      if (m_pEntry == 0 && m_pParent->m_CacheFull)
186        m_pParent = 0;
187      return *this;
188    }
189    m_pParent = 0;
190    return *this;
191  }
192
193  m_pEntry = m_Iter.getEntry();
194  return *this;
195}
196
197DirIterator DirIterator::operator++(int pIn) {
198  DirIterator tmp(*this);
199
200  // move forward one step first.
201  ++m_Iter;
202
203  if (m_pParent->m_Cache.end() == m_Iter) {
204    if (!m_pParent->m_CacheFull) {
205      m_pEntry = detail::bring_one_into_cache(*this);
206      if (m_pEntry == 0 && m_pParent->m_CacheFull)
207        m_pParent = 0;
208      return tmp;
209    }
210    m_pParent = 0;
211    return tmp;
212  }
213
214  m_pEntry = m_Iter.getEntry();
215  return tmp;
216}
217
218bool DirIterator::operator==(const DirIterator& y) const {
219  if (m_pParent != y.m_pParent)
220    return false;
221  if (m_pParent == 0)
222    return true;
223  const Path* x_path = path();
224  const Path* y_path = y.path();
225  if (x_path == 0 && y_path == 0)
226    return true;
227  if (x_path == 0 || y_path == 0)
228    return false;
229  return (*x_path == *y_path);
230}
231
232bool DirIterator::operator!=(const DirIterator& y) const {
233  return !this->operator==(y);
234}
235
236}  // namespace fs
237}  // namespace sys
238}  // namespace mcld
239