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