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