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