Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / native_client_sdk / src / libraries / nacl_io / path.cc
blobe75f0683831cd8bef5ec73c6f45ca93fc085866d
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.
5 #include "nacl_io/path.h"
7 #include <assert.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <string>
12 #include "sdk_util/string_util.h"
14 namespace nacl_io {
16 Path::Path() : len_(0) {
17 path_[0] = 0;
20 Path::Path(const Path& path) {
21 len_ = path.len_;
22 strcpy(path_, path.path_);
25 Path::Path(const std::string& path) {
26 Set(path);
29 bool Path::IsAbsolute() const {
30 return path_[0] == '/';
33 std::string Path::Part(size_t index) const {
34 if (IsAbsolute() && index == 0) {
35 return std::string("/");
38 const char* start = &path_[0];
39 size_t slashes = 0;
40 const char* p;
41 for (p = &path_[0]; *p; p++) {
42 if (*p == '/') {
43 if (++slashes == index + 1)
44 break;
46 start = p + 1;
50 return std::string(start, p - start);
53 size_t Path::Size() const {
54 if (len_ == 0)
55 return 0;
57 const char* p = &path_[0];
58 if (len_ == 1 && *p == '/') {
59 return 1;
62 size_t count = 1;
63 for (; *p; p++) {
64 if (*p == '/')
65 count++;
67 return count;
70 bool Path::IsRoot() const {
71 return strcmp(path_, "/") == 0;
74 Path& Path::MakeRelative() {
75 if (IsAbsolute()) {
76 memmove(&path_[0], &path_[1], PATH_MAX - 1);
77 len_--;
79 return *this;
82 Path& Path::Append(const Path& path) {
83 // Appending an absolute path effectivly sets the path, ignoring
84 // the current contents.
85 if (path.IsAbsolute()) {
86 strcpy(path_, path.path_);
87 } else {
88 strncat(path_, "/", PATH_MAX - len_ - 1);
89 len_++;
90 strncat(path_, path.path_, PATH_MAX - len_ - 1);
91 len_ += path.len_;
93 if (len_ >= PATH_MAX - 1) {
94 len_ = PATH_MAX - 1;
98 Normalize();
99 return *this;
102 Path& Path::Append(const std::string& path) {
103 return Append(Path(path));
106 Path& Path::Set(const std::string& path) {
107 strncpy(path_, path.c_str(), PATH_MAX - 1);
108 path_[PATH_MAX - 1] = 0;
109 len_ = path.length();
110 if (len_ > PATH_MAX - 1)
111 len_ = PATH_MAX - 1;
112 Normalize();
113 return *this;
116 Path Path::Parent() const {
117 const char* last_slash = strrchr(path_, '/');
118 if (last_slash) {
119 Path out;
120 if (last_slash == &path_[0]) {
121 out.len_ = 1;
122 strcpy(out.path_, "/");
123 } else {
124 out.len_ = last_slash - &path_[0];
125 strncpy(out.path_, path_, out.len_);
126 out.path_[out.len_] = 0;
129 return out;
132 return Path(*this);
135 std::string Path::Basename() const {
136 if (IsRoot())
137 return std::string(path_);
139 const char* last_slash = strrchr(path_, '/');
140 if (last_slash)
141 return std::string(last_slash + 1, path_ + len_ - (last_slash + 1));
143 return std::string(path_);
146 std::string Path::Join() const {
147 return std::string(path_);
150 std::string Path::Range(size_t start, size_t end) const {
151 assert(start <= end);
153 const char* pstart = &path_[0];
154 const char* pend = &path_[len_];
156 if (IsAbsolute() && start == 0 && end == 1)
157 return std::string("/");
159 size_t slashes = 0;
160 for (const char* p = &path_[0]; *p; p++) {
161 if (*p == '/') {
162 ++slashes;
163 if (slashes == start)
164 pstart = p + 1;
166 if (slashes == end) {
167 pend = p;
168 break;
173 if (slashes < start || pstart > pend)
174 return std::string();
176 return std::string(pstart, pend - pstart);
179 void Path::Normalize() {
180 char* outp = &path_[0];
181 const char* start = outp;
182 const char* part_start = start;
183 const char* next_slash;
184 bool is_absolute = false;
186 if (IsAbsolute()) {
187 // Absolute path. Append the slash, then continue the algorithm as if the
188 // path were relative.
189 start++;
190 outp++;
191 part_start++;
192 is_absolute = true;
195 do {
196 next_slash = strchr(part_start, '/');
197 const char* part_end = next_slash;
198 if (!part_end)
199 part_end = part_start + strlen(part_start);
201 size_t part_len = part_end - part_start;
203 bool should_append = true;
204 if (part_len == 0) {
205 // Don't append if the part is empty.
206 should_append = false;
207 } else if (part_len == 1 && part_start[0] == '.') {
208 // Don't append "."
209 should_append = false;
210 } else if (part_len == 2 && part_start[0] == '.' && part_start[1] == '.') {
211 // If part is "..", only append if the output is empty or already has
212 // ".." at the end.
213 if (outp == start ||
214 (outp - start >= 2 && outp[-1] == '.' && outp[-2] == '.')) {
215 should_append = !is_absolute;
216 } else {
217 should_append = false;
218 // Move outp backward to the one past the previous slash, or to the
219 // beginning of the string. Unless outp == start, outp[-1] is a '/'.
220 if (outp > start)
221 --outp;
222 while (outp > start && outp[0] != '/')
223 --outp;
227 if (should_append) {
228 // Append [part_start, part_end) to outp.
229 if (outp != start) {
230 // Append slash to separate from previous path.
231 *outp++ = '/';
234 // Only need to copy bytes when the pointers are different.
235 if (outp != part_start) {
236 memmove(outp, part_start, part_len);
239 outp += part_len;
242 part_start = next_slash + 1;
243 } while (next_slash);
245 // Return '.' instead of an empty path.
246 if (outp == start && !is_absolute) {
247 *outp++ = '.';
250 *outp = 0;
251 len_ = outp - &path_[0];
254 Path& Path::operator=(const Path& p) {
255 len_ = p.len_;
256 strcpy(path_, p.path_);
257 return *this;
260 Path& Path::operator=(const std::string& p) {
261 return Set(p);
264 bool Path::operator==(const Path& other) {
265 return len_ == other.len_ && strncmp(path_, other.path_, len_) == 0;
268 bool Path::operator!=(const Path& other) {
269 return !operator==(other);
272 } // namespace nacl_io