blob: 06e11bee857f4dad49376828dfedc01e5600e098 [file] [log] [blame]
// Copyright 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "base/PathUtils.h"
#include <string.h> // for size_t, strncmp
#include <iterator> // for reverse_iterator, operator!=
#include <numeric> // for accumulate
#include <type_traits> // for enable_if<>::type
#ifndef _WIN32
#include <unistd.h>
#endif
#ifdef _WIN32
#include "Win32UnicodeString.h"
#endif
static inline bool sIsEmpty(const char* str) {
return !str || str[0] == '\0';
}
namespace android {
namespace base {
const char* const PathUtils::kExeNameSuffixes[kHostTypeCount] = {"", ".exe"};
const char* const PathUtils::kExeNameSuffix =
PathUtils::kExeNameSuffixes[PathUtils::HOST_TYPE];
std::string PathUtils::toExecutableName(const char* baseName,
HostType hostType) {
return static_cast<std::string>(baseName).append(
kExeNameSuffixes[hostType]);
}
// static
bool PathUtils::isDirSeparator(int ch, HostType hostType) {
return (ch == '/') || (hostType == HOST_WIN32 && ch == '\\');
}
// static
bool PathUtils::isPathSeparator(int ch, HostType hostType) {
return (hostType == HOST_POSIX && ch == ':') ||
(hostType == HOST_WIN32 && ch == ';');
}
// static
size_t PathUtils::rootPrefixSize(const std::string& path, HostType hostType) {
if (path.empty()) return 0;
if (hostType != HOST_WIN32)
return (path[0] == '/') ? 1U : 0U;
size_t result = 0;
if (path[1] == ':') {
int ch = path[0];
if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
result = 2U;
} else if (!strncmp(path.c_str(), "\\\\.\\", 4) ||
!strncmp(path.c_str(), "\\\\?\\", 4)) {
// UNC prefixes.
return 4U;
} else if (isDirSeparator(path[0], hostType)) {
result = 1;
if (isDirSeparator(path[1], hostType)) {
result = 2;
while (path[result] && !isDirSeparator(path[result], HOST_WIN32))
result++;
}
}
if (result && path[result] && isDirSeparator(path[result], HOST_WIN32))
result++;
return result;
}
// static
bool PathUtils::isAbsolute(const char* path, HostType hostType) {
size_t prefixSize = rootPrefixSize(path, hostType);
if (!prefixSize) {
return false;
}
if (hostType != HOST_WIN32) {
return true;
}
return isDirSeparator(path[prefixSize - 1], HOST_WIN32);
}
// static
std::string PathUtils::removeTrailingDirSeparator(const char* path,
HostType hostType) {
size_t pathLen = strlen(path);
// NOTE: Don't remove initial path separator for absolute paths.
while (pathLen > 1U && isDirSeparator(path[pathLen - 1U], hostType)) {
pathLen--;
}
return std::string(path, pathLen);
}
// static
std::string PathUtils::addTrailingDirSeparator(const char* path,
HostType hostType) {
std::string result = path;
if (result.size() > 0 && !isDirSeparator(result[result.size() - 1U])) {
result += getDirSeparator(hostType);
}
return result;
}
// static
bool PathUtils::split(const char* path,
HostType hostType,
const char** dirName,
const char** baseName) {
if (sIsEmpty(path)) {
return false;
}
// If there is a trailing directory separator, return an error.
size_t end = strlen(path);
if (isDirSeparator(path[end - 1U], hostType)) {
return false;
}
// Find last separator.
size_t prefixLen = rootPrefixSize(path, hostType);
size_t pos = end;
while (pos > prefixLen && !isDirSeparator(path[pos - 1U], hostType)) {
pos--;
}
// Handle common case.
if (pos > prefixLen) {
if (dirName) {
*dirName = path;
}
if (baseName) {
*baseName = path + pos;
}
return true;
}
// If there is no directory separator, the path is a single file name.
if (dirName) {
if (!prefixLen) {
*dirName = ".";
} else {
*dirName = path;
}
}
if (baseName) {
*baseName = path + prefixLen;
}
return true;
}
// static
std::string PathUtils::join(const std::string& path1,
const std::string& path2,
HostType hostType) {
if (path1.empty()) {
return path2;
}
if (path2.empty()) {
return path1;
}
if (isAbsolute(path2.c_str(), hostType)) {
return path2;
}
size_t prefixLen = rootPrefixSize(path1, hostType);
std::string result(path1);
size_t end = result.size();
if (end > prefixLen && !isDirSeparator(result[end - 1U], hostType)) {
result += getDirSeparator(hostType);
}
result += path2;
return result;
}
// static
template <class String>
std::vector<String> PathUtils::decompose(const String& path,
HostType hostType) {
std::vector<String> result;
if (path.empty())
return result;
size_t prefixLen = rootPrefixSize(path, hostType);
auto it = path.begin();
if (prefixLen) {
result.emplace_back(it, it + prefixLen);
it += prefixLen;
}
for (;;) {
auto p = it;
while (*p && !isDirSeparator(*p, hostType))
p++;
if (p > it) {
result.emplace_back(it, p);
}
if (!*p) {
break;
}
it = p + 1;
}
return result;
}
std::vector<std::string> PathUtils::decompose(std::string&& path,
HostType hostType) {
return decompose<std::string>(path, hostType);
}
std::vector<std::string> PathUtils::decompose(const std::string& path,
HostType hostType) {
return decompose<std::string>(path, hostType);
}
template <class String>
std::string PathUtils::recompose(const std::vector<String>& components,
HostType hostType) {
if (components.empty()) {
return {};
}
const char dirSeparator = getDirSeparator(hostType);
std::string result;
// To reduce memory allocations, compute capacity before doing the
// real append.
const size_t capacity =
components.size() - 1 +
std::accumulate(components.begin(), components.end(), size_t(0),
[](size_t val, const String& next) {
return val + next.size();
});
result.reserve(capacity);
bool addSeparator = false;
for (size_t n = 0; n < components.size(); ++n) {
const auto& component = components[n];
if (addSeparator)
result += dirSeparator;
addSeparator = true;
if (n == 0) {
size_t prefixLen = rootPrefixSize(component, hostType);
if (prefixLen == component.size()) {
addSeparator = false;
}
}
result += component;
}
return result;
}
// static
std::string PathUtils::recompose(const std::vector<std::string>& components,
HostType hostType) {
return recompose<std::string>(components, hostType);
}
// static
template <class String>
void PathUtils::simplifyComponents(std::vector<String>* components) {
std::vector<String> stack;
for (auto& component : *components) {
if (component == ".") {
// Ignore any instance of '.' from the list.
continue;
}
if (component == "..") {
// Handling of '..' is specific: if there is a item on the
// stack that is not '..', then remove it, otherwise push
// the '..'.
if (!stack.empty() && stack.back() != "..") {
stack.pop_back();
} else {
stack.push_back(std::move(component));
}
continue;
}
// If not a '..', just push on the stack.
stack.push_back(std::move(component));
}
if (stack.empty()) {
stack.push_back(".");
}
components->swap(stack);
}
void PathUtils::simplifyComponents(std::vector<std::string>* components) {
simplifyComponents<std::string>(components);
}
#ifdef _WIN32
// Return |path| as a Unicode string, while discarding trailing separators.
Win32UnicodeString win32Path(const char* path) {
Win32UnicodeString wpath(path);
// Get rid of trailing directory separators, Windows doesn't like them.
size_t size = wpath.size();
while (size > 0U &&
(wpath[size - 1U] == L'\\' || wpath[size - 1U] == L'/')) {
size--;
}
if (size < wpath.size()) {
wpath.resize(size);
}
return wpath;
}
/* access function */
#define F_OK 0 /* test for existence of file */
#define X_OK 0x01 /* test for execute or search permission */
#define W_OK 0x02 /* test for write permission */
#define R_OK 0x04 /* test for read permission */
static int GetWin32Mode(int mode) {
// Convert |mode| to win32 permission bits.
int win32mode = 0x0;
if ((mode & R_OK) || (mode & X_OK)) {
win32mode |= 0x4;
}
if (mode & W_OK) {
win32mode |= 0x2;
}
return win32mode;
}
#endif
bool pathExists(const char* path) {
#ifdef _WIN32
return _waccess(win32Path(path).c_str(), GetWin32Mode(F_OK));
#else
return 0 == access(path, F_OK);
#endif
}
std::string pj(const std::vector<std::string>& paths) {
std::string res;
if (paths.size() == 0)
return "";
if (paths.size() == 1)
return paths[0];
res = paths[0];
for (size_t i = 1; i < paths.size(); i++) {
res = PathUtils::join(res, paths[i]);
}
return res;
}
std::string PathUtils::addTrailingDirSeparator(const std::string& path,
HostType hostType) {
std::string result = path;
if (result.size() > 0 && !isDirSeparator(result[result.size() - 1U])) {
result += getDirSeparator(hostType);
}
return result;
}
} // namespace base
} // namespace android