path.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "nacl_io/path.h"
6
7#include <stdio.h>
8#include <string.h>
9#include <string>
10
11#include "sdk_util/string_util.h"
12
13namespace nacl_io {
14
15Path::Path() {
16}
17
18Path::Path(const Path& path) {
19  paths_ = path.paths_;
20}
21
22Path::Path(const std::string& path) {
23  Set(path);
24}
25
26Path::~Path() {
27}
28
29bool Path::IsAbsolute() const {
30  return !paths_.empty() && paths_[0] == "/";
31}
32
33const std::string& Path::Part(size_t index) const {
34  return paths_[index];
35}
36
37size_t Path::Size() const {
38  return paths_.size();
39}
40
41bool Path::Top() const {
42  return (paths_.size() == 0) || (paths_.size() == 1 && paths_[0] == "/");
43}
44
45Path& Path::Append(const std::string& path) {
46  StringArray_t paths = Split(path);
47
48  for (size_t index = 0; index < paths.size(); index++) {
49    // Skip ROOT
50    if (paths_.size() && index == 0 && paths[0] == "/")
51      continue;
52    paths_.push_back(paths[index]);
53  }
54
55  paths_ = Normalize(paths_);
56  return *this;
57}
58
59Path& Path::Prepend(const std::string& path) {
60  StringArray_t paths = Split(path);
61
62  for (size_t index = 0; index < paths_.size(); index++) {
63    // Skip ROOT
64    if (index == 0 && paths_[0] == "/")
65      continue;
66    paths.push_back(paths[index]);
67  }
68  paths_ = Normalize(paths);
69  return *this;
70}
71
72Path& Path::Set(const std::string& path) {
73  StringArray_t paths = Split(path);
74  paths_ = Normalize(paths);
75  return *this;
76}
77
78Path Path::Parent() const {
79  Path out;
80  out.paths_ = paths_;
81  if (out.paths_.size())
82    out.paths_.pop_back();
83  return out;
84}
85
86std::string Path::Basename() const {
87  if (paths_.size())
88    return paths_.back();
89  return std::string();
90}
91
92std::string Path::Join() const {
93  return Range(paths_, 0, paths_.size());
94}
95
96std::string Path::Range(size_t start, size_t end) const {
97  return Range(paths_, start, end);
98}
99
100StringArray_t Path::Split() const {
101  return paths_;
102}
103
104// static
105StringArray_t Path::Normalize(const StringArray_t& paths) {
106  StringArray_t path_out;
107
108  for (size_t index = 0; index < paths.size(); index++) {
109    const std::string& curr = paths[index];
110
111    // Check if '/' was used excessively in the path.
112    // For example, in cd Desktop/////
113    if (curr == "/" && index != 0)
114      continue;
115
116    // Check for '.' in the path and remove it
117    if (curr == ".")
118      continue;
119
120    // Check for '..'
121    if (curr == "..") {
122      // If the path is empty, or "..", then add ".."
123      if (path_out.empty() || path_out.back() == "..") {
124        path_out.push_back(curr);
125        continue;
126      }
127
128      // If the path is at root, "/.." = "/"
129      if (path_out.back() == "/") {
130        continue;
131      }
132
133      // if we are already at root, then stay there (root/.. -> root)
134      if (path_out.back() == "/") {
135        continue;
136      }
137
138      // otherwise, pop off the top path component
139      path_out.pop_back();
140      continue;
141    }
142
143    // By now, we should have handled end cases so just append.
144    path_out.push_back(curr);
145  }
146
147  // If the path was valid, but now it's empty, return self
148  if (path_out.size() == 0)
149    path_out.push_back(".");
150
151  return path_out;
152}
153
154// static
155std::string Path::Join(const StringArray_t& paths) {
156  return Range(paths, 0, paths.size());
157}
158
159// static
160std::string Path::Range(const StringArray_t& paths, size_t start, size_t end) {
161  std::string out_path;
162  size_t index = start;
163
164  if (end > paths.size())
165    end = paths.size();
166
167  // If this is an absolute path, paths[0] == "/". In this case, we don't want
168  // to add an additional / separator.
169  if (start == 0 && end > 0 && paths[0] == "/") {
170    out_path += "/";
171    index++;
172  }
173
174  for (; index < end; index++) {
175    out_path += paths[index];
176    if (index < end - 1)
177      out_path += "/";
178  }
179
180  return out_path;
181}
182
183// static
184StringArray_t Path::Split(const std::string& path) {
185  StringArray_t path_split;
186  StringArray_t components;
187
188  sdk_util::SplitString(path, '/', &path_split);
189
190  if (path[0] == '/')
191    components.push_back("/");
192
193  // Copy path_split to components, removing empty path segments.
194  for (StringArray_t::const_iterator it = path_split.begin();
195       it != path_split.end();
196       ++it) {
197    if (!it->empty())
198      components.push_back(*it);
199  }
200  return components;
201}
202
203Path& Path::operator=(const Path& p) {
204  paths_ = p.paths_;
205  return *this;
206}
207
208Path& Path::operator=(const std::string& p) {
209  return Set(p);
210}
211
212}  // namespace nacl_io
213