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