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 <assert.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <string>
11 
12 #include "sdk_util/string_util.h"
13 
14 namespace nacl_io {
15 
Path()16 Path::Path() : len_(0) {
17   path_[0] = 0;
18 }
19 
Path(const Path & path)20 Path::Path(const Path& path) {
21   len_ = path.len_;
22   strcpy(path_, path.path_);
23 }
24 
Path(const std::string & path)25 Path::Path(const std::string& path) {
26   Set(path);
27 }
28 
IsAbsolute() const29 bool Path::IsAbsolute() const {
30   return path_[0] == '/';
31 }
32 
Part(size_t index) const33 std::string Path::Part(size_t index) const {
34   if (IsAbsolute() && index == 0) {
35     return std::string("/");
36   }
37 
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;
45 
46       start = p + 1;
47     }
48   }
49 
50   return std::string(start, p - start);
51 }
52 
Size() const53 size_t Path::Size() const {
54   if (len_ == 0)
55     return 0;
56 
57   const char* p = &path_[0];
58   if (len_ == 1 && *p == '/') {
59     return 1;
60   }
61 
62   size_t count = 1;
63   for (; *p; p++) {
64     if (*p == '/')
65       count++;
66   }
67   return count;
68 }
69 
IsRoot() const70 bool Path::IsRoot() const {
71   return strcmp(path_, "/") == 0;
72 }
73 
MakeRelative()74 Path& Path::MakeRelative() {
75   if (IsAbsolute()) {
76     memmove(&path_[0], &path_[1], PATH_MAX - 1);
77     len_--;
78   }
79   return *this;
80 }
81 
Append(const Path & path)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_;
92 
93     if (len_ >= PATH_MAX - 1) {
94       len_ = PATH_MAX - 1;
95     }
96   }
97 
98   Normalize();
99   return *this;
100 }
101 
Append(const std::string & path)102 Path& Path::Append(const std::string& path) {
103   return Append(Path(path));
104 }
105 
Set(const std::string & 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;
114 }
115 
Parent() const116 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;
127     }
128 
129     return out;
130   }
131 
132   return Path(*this);
133 }
134 
Basename() const135 std::string Path::Basename() const {
136   if (IsRoot())
137     return std::string(path_);
138 
139   const char* last_slash = strrchr(path_, '/');
140   if (last_slash)
141     return std::string(last_slash + 1, path_ + len_ - (last_slash + 1));
142 
143   return std::string(path_);
144 }
145 
Join() const146 std::string Path::Join() const {
147   return std::string(path_);
148 }
149 
Range(size_t start,size_t end) const150 std::string Path::Range(size_t start, size_t end) const {
151   assert(start <= end);
152 
153   const char* pstart = &path_[0];
154   const char* pend = &path_[len_];
155 
156   if (IsAbsolute() && start == 0 && end == 1)
157     return std::string("/");
158 
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;
165 
166       if (slashes == end) {
167         pend = p;
168         break;
169       }
170     }
171   }
172 
173   if (slashes < start || pstart > pend)
174     return std::string();
175 
176   return std::string(pstart, pend - pstart);
177 }
178 
Normalize()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;
185 
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;
193   }
194 
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);
200 
201     size_t part_len = part_end - part_start;
202 
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;
224       }
225     }
226 
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++ = '/';
232       }
233 
234       // Only need to copy bytes when the pointers are different.
235       if (outp != part_start) {
236         memmove(outp, part_start, part_len);
237       }
238 
239       outp += part_len;
240     }
241 
242     part_start = next_slash + 1;
243   } while (next_slash);
244 
245   // Return '.' instead of an empty path.
246   if (outp == start && !is_absolute) {
247     *outp++ = '.';
248   }
249 
250   *outp = 0;
251   len_ = outp - &path_[0];
252 }
253 
operator =(const Path & p)254 Path& Path::operator=(const Path& p) {
255   len_ = p.len_;
256   strcpy(path_, p.path_);
257   return *this;
258 }
259 
operator =(const std::string & p)260 Path& Path::operator=(const std::string& p) {
261   return Set(p);
262 }
263 
operator ==(const Path & other)264 bool Path::operator==(const Path& other) {
265   return len_ == other.len_ && strncmp(path_, other.path_, len_) == 0;
266 }
267 
operator !=(const Path & other)268 bool Path::operator!=(const Path& other) {
269   return !operator==(other);
270 }
271 
272 }  // namespace nacl_io
273