1//===- Path.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/Config/Config.h>
10#include <mcld/Support/FileSystem.h>
11#include <mcld/Support/Path.h>
12#include <llvm/ADT/StringRef.h>
13
14#include <locale>
15#include <string.h>
16#include <istream>
17#include <ostream>
18
19using namespace mcld;
20using namespace mcld::sys::fs;
21
22//===--------------------------------------------------------------------===//
23// Helper
24//===--------------------------------------------------------------------===//
25namespace {
26#if defined(MCLD_ON_WIN32)
27bool is_separator(char value)
28{
29  return (value == separator || value == preferred_separator);
30}
31
32const Path::StringType separator_str("/");
33
34#else
35bool is_separator(char value)
36{
37  return (value == separator);
38}
39
40const Path::StringType separator_str("/");
41
42#endif
43} // anonymous namespace
44
45
46//===--------------------------------------------------------------------===//
47// Path
48//===--------------------------------------------------------------------===//
49Path::Path()
50  : m_PathName() {
51}
52
53Path::Path(const Path::ValueType* s )
54  : m_PathName(s) {
55}
56
57Path::Path(const Path::StringType &s )
58  : m_PathName(s) {
59}
60
61Path::Path(const Path& pCopy)
62 : m_PathName(pCopy.m_PathName) {
63}
64
65Path::~Path()
66{
67}
68
69bool Path::isFromRoot() const
70{
71  if (m_PathName.empty())
72    return false;
73  return (separator == m_PathName[0]);
74}
75
76bool Path::isFromPWD() const
77{
78  if (2 > m_PathName.size())
79    return false;
80  return ('.' == m_PathName[0] && separator == m_PathName[1]);
81}
82
83Path& Path::assign(const Path::StringType &s)
84{
85  m_PathName.assign(s);
86  return *this;
87}
88
89Path& Path::assign(const Path::ValueType* s, unsigned int length)
90{
91  if (0 == s || 0 == length)
92    assert(0 && "assign a null or empty string to Path");
93  m_PathName.assign(s, length);
94  return *this;
95}
96
97//a,/b a/,b a/,b/ a,b is a/b
98Path& Path::append(const Path& pPath)
99{
100  //first path is a/,second path is /b
101  if(m_PathName[m_PathName.length()-1] == separator &&
102     pPath.native()[0] == separator) {
103    unsigned int old_size = m_PathName.size()-1;
104    unsigned int new_size = old_size + pPath.native().size();
105
106    m_PathName.resize(new_size);
107    strcpy(const_cast<ValueType*>(m_PathName.data()+old_size), pPath.native().data());
108  }
109  //first path is a,second path is b
110  else if(this->native()[this->native().size()-1] != separator &&
111          pPath.native()[0] != separator) {
112    m_PathName.append(separator_str);
113    m_PathName.append(pPath.native());
114  }
115  // a/,b or a,/b just append
116  else {
117    m_PathName.append(pPath.native());
118  }
119  return *this;
120}
121
122bool Path::empty() const
123{
124  return m_PathName.empty();
125}
126
127Path::StringType Path::generic_string() const
128{
129  StringType result = m_PathName;
130  detail::canonicalize(result);
131  return result;
132}
133
134bool Path::canonicalize()
135{
136  return detail::canonicalize(m_PathName);
137}
138
139Path::StringType::size_type Path::m_append_separator_if_needed()
140{
141#if defined(MCLD_ON_WIN32)
142  // On Windows platform, path can not append separator.
143  return 0;
144#endif
145
146  StringType::value_type last_char = m_PathName[m_PathName.size() - 1];
147  if (!m_PathName.empty() &&
148      !is_separator(last_char)) {
149    StringType::size_type tmp(m_PathName.size());
150    m_PathName += separator_str;
151    return tmp;
152  }
153  return 0;
154}
155
156void Path::m_erase_redundant_separator(Path::StringType::size_type pSepPos)
157{
158  size_t begin=pSepPos;
159  // skip '/' or '\\'
160  while(separator == m_PathName[pSepPos]) {
161#if defined(MCLD_ON_WIN32)
162    pSepPos += 2;
163#else
164    ++pSepPos;
165#endif
166  }
167
168  if(begin!=pSepPos)
169    m_PathName.erase(begin+1,pSepPos-begin-1);
170}
171
172Path Path::parent_path() const
173{
174  size_t end_pos = m_PathName.find_last_of(separator);
175  if (end_pos != StringType::npos)
176    return Path(m_PathName.substr(0, end_pos));
177  return Path();
178}
179
180Path Path::filename() const
181{
182  size_t pos = m_PathName.find_last_of(separator);
183  if (pos != StringType::npos) {
184    ++pos;
185    return Path(m_PathName.substr(pos));
186  }
187  return Path(*this);
188}
189
190Path Path::stem() const
191{
192  size_t begin_pos = m_PathName.find_last_of(separator)+1;
193  size_t end_pos   = m_PathName.find_last_of(dot);
194  Path result_path(m_PathName.substr(begin_pos, end_pos - begin_pos));
195  return result_path;
196}
197
198Path Path::extension() const
199{
200  size_t pos = m_PathName.find_last_of('.');
201  if (pos == StringType::npos)
202    return Path();
203  return Path(m_PathName.substr(pos));
204}
205
206//===--------------------------------------------------------------------===//
207// non-member functions
208//===--------------------------------------------------------------------===//
209bool mcld::sys::fs::operator==(const Path& pLHS,const Path& pRHS)
210{
211  return (pLHS.generic_string()==pRHS.generic_string());
212}
213
214bool mcld::sys::fs::operator!=(const Path& pLHS,const Path& pRHS)
215{
216  return !(pLHS==pRHS);
217}
218
219Path mcld::sys::fs::operator+(const Path& pLHS, const Path& pRHS)
220{
221  mcld::sys::fs::Path result = pLHS;
222  result.append(pRHS);
223  return result;
224}
225
226