blob: e75f0683831cd8bef5ec73c6f45ca93fc085866d [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "nacl_io/path.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include "sdk_util/string_util.h"
namespace nacl_io {
Path::Path() : len_(0) {
path_[0] = 0;
}
Path::Path(const Path& path) {
len_ = path.len_;
strcpy(path_, path.path_);
}
Path::Path(const std::string& path) {
Set(path);
}
bool Path::IsAbsolute() const {
return path_[0] == '/';
}
std::string Path::Part(size_t index) const {
if (IsAbsolute() && index == 0) {
return std::string("/");
}
const char* start = &path_[0];
size_t slashes = 0;
const char* p;
for (p = &path_[0]; *p; p++) {
if (*p == '/') {
if (++slashes == index + 1)
break;
start = p + 1;
}
}
return std::string(start, p - start);
}
size_t Path::Size() const {
if (len_ == 0)
return 0;
const char* p = &path_[0];
if (len_ == 1 && *p == '/') {
return 1;
}
size_t count = 1;
for (; *p; p++) {
if (*p == '/')
count++;
}
return count;
}
bool Path::IsRoot() const {
return strcmp(path_, "/") == 0;
}
Path& Path::MakeRelative() {
if (IsAbsolute()) {
memmove(&path_[0], &path_[1], PATH_MAX - 1);
len_--;
}
return *this;
}
Path& Path::Append(const Path& path) {
// Appending an absolute path effectivly sets the path, ignoring
// the current contents.
if (path.IsAbsolute()) {
strcpy(path_, path.path_);
} else {
strncat(path_, "/", PATH_MAX - len_ - 1);
len_++;
strncat(path_, path.path_, PATH_MAX - len_ - 1);
len_ += path.len_;
if (len_ >= PATH_MAX - 1) {
len_ = PATH_MAX - 1;
}
}
Normalize();
return *this;
}
Path& Path::Append(const std::string& path) {
return Append(Path(path));
}
Path& Path::Set(const std::string& path) {
strncpy(path_, path.c_str(), PATH_MAX - 1);
path_[PATH_MAX - 1] = 0;
len_ = path.length();
if (len_ > PATH_MAX - 1)
len_ = PATH_MAX - 1;
Normalize();
return *this;
}
Path Path::Parent() const {
const char* last_slash = strrchr(path_, '/');
if (last_slash) {
Path out;
if (last_slash == &path_[0]) {
out.len_ = 1;
strcpy(out.path_, "/");
} else {
out.len_ = last_slash - &path_[0];
strncpy(out.path_, path_, out.len_);
out.path_[out.len_] = 0;
}
return out;
}
return Path(*this);
}
std::string Path::Basename() const {
if (IsRoot())
return std::string(path_);
const char* last_slash = strrchr(path_, '/');
if (last_slash)
return std::string(last_slash + 1, path_ + len_ - (last_slash + 1));
return std::string(path_);
}
std::string Path::Join() const {
return std::string(path_);
}
std::string Path::Range(size_t start, size_t end) const {
assert(start <= end);
const char* pstart = &path_[0];
const char* pend = &path_[len_];
if (IsAbsolute() && start == 0 && end == 1)
return std::string("/");
size_t slashes = 0;
for (const char* p = &path_[0]; *p; p++) {
if (*p == '/') {
++slashes;
if (slashes == start)
pstart = p + 1;
if (slashes == end) {
pend = p;
break;
}
}
}
if (slashes < start || pstart > pend)
return std::string();
return std::string(pstart, pend - pstart);
}
void Path::Normalize() {
char* outp = &path_[0];
const char* start = outp;
const char* part_start = start;
const char* next_slash;
bool is_absolute = false;
if (IsAbsolute()) {
// Absolute path. Append the slash, then continue the algorithm as if the
// path were relative.
start++;
outp++;
part_start++;
is_absolute = true;
}
do {
next_slash = strchr(part_start, '/');
const char* part_end = next_slash;
if (!part_end)
part_end = part_start + strlen(part_start);
size_t part_len = part_end - part_start;
bool should_append = true;
if (part_len == 0) {
// Don't append if the part is empty.
should_append = false;
} else if (part_len == 1 && part_start[0] == '.') {
// Don't append "."
should_append = false;
} else if (part_len == 2 && part_start[0] == '.' && part_start[1] == '.') {
// If part is "..", only append if the output is empty or already has
// ".." at the end.
if (outp == start ||
(outp - start >= 2 && outp[-1] == '.' && outp[-2] == '.')) {
should_append = !is_absolute;
} else {
should_append = false;
// Move outp backward to the one past the previous slash, or to the
// beginning of the string. Unless outp == start, outp[-1] is a '/'.
if (outp > start)
--outp;
while (outp > start && outp[0] != '/')
--outp;
}
}
if (should_append) {
// Append [part_start, part_end) to outp.
if (outp != start) {
// Append slash to separate from previous path.
*outp++ = '/';
}
// Only need to copy bytes when the pointers are different.
if (outp != part_start) {
memmove(outp, part_start, part_len);
}
outp += part_len;
}
part_start = next_slash + 1;
} while (next_slash);
// Return '.' instead of an empty path.
if (outp == start && !is_absolute) {
*outp++ = '.';
}
*outp = 0;
len_ = outp - &path_[0];
}
Path& Path::operator=(const Path& p) {
len_ = p.len_;
strcpy(path_, p.path_);
return *this;
}
Path& Path::operator=(const std::string& p) {
return Set(p);
}
bool Path::operator==(const Path& other) {
return len_ == other.len_ && strncmp(path_, other.path_, len_) == 0;
}
bool Path::operator!=(const Path& other) {
return !operator==(other);
}
} // namespace nacl_io