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