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