16d528ed9Sopenharmony_ci// Copyright (c) 2012 The Chromium Authors. All rights reserved. 26d528ed9Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be 36d528ed9Sopenharmony_ci// found in the LICENSE file. 46d528ed9Sopenharmony_ci 56d528ed9Sopenharmony_ci#include "base/files/file_path.h" 66d528ed9Sopenharmony_ci 76d528ed9Sopenharmony_ci#include <string.h> 86d528ed9Sopenharmony_ci 96d528ed9Sopenharmony_ci#include <algorithm> 106d528ed9Sopenharmony_ci#include <iterator> 116d528ed9Sopenharmony_ci#include <string> 126d528ed9Sopenharmony_ci#include <string_view> 136d528ed9Sopenharmony_ci 146d528ed9Sopenharmony_ci#include "base/logging.h" 156d528ed9Sopenharmony_ci#include "base/strings/string_util.h" 166d528ed9Sopenharmony_ci#include "base/strings/utf_string_conversions.h" 176d528ed9Sopenharmony_ci#include "util/build_config.h" 186d528ed9Sopenharmony_ci 196d528ed9Sopenharmony_ci#if defined(OS_MACOSX) 206d528ed9Sopenharmony_ci#include "base/mac/scoped_cftyperef.h" 216d528ed9Sopenharmony_ci#include "base/third_party/icu/icu_utf.h" 226d528ed9Sopenharmony_ci#endif 236d528ed9Sopenharmony_ci 246d528ed9Sopenharmony_ci#if defined(OS_WIN) 256d528ed9Sopenharmony_ci#include <windows.h> 266d528ed9Sopenharmony_ci#elif defined(OS_MACOSX) 276d528ed9Sopenharmony_ci#include <CoreFoundation/CoreFoundation.h> 286d528ed9Sopenharmony_ci#endif 296d528ed9Sopenharmony_ci 306d528ed9Sopenharmony_cinamespace base { 316d528ed9Sopenharmony_ci 326d528ed9Sopenharmony_ciusing StringType = FilePath::StringType; 336d528ed9Sopenharmony_ciusing StringViewType = FilePath::StringViewType; 346d528ed9Sopenharmony_ci 356d528ed9Sopenharmony_cinamespace { 366d528ed9Sopenharmony_ci 376d528ed9Sopenharmony_ciconst char* const kCommonDoubleExtensionSuffixes[] = {"gz", "z", "bz2", "bz"}; 386d528ed9Sopenharmony_ciconst char* const kCommonDoubleExtensions[] = {"user.js"}; 396d528ed9Sopenharmony_ci 406d528ed9Sopenharmony_ciconst FilePath::CharType kStringTerminator = FILE_PATH_LITERAL('\0'); 416d528ed9Sopenharmony_ci 426d528ed9Sopenharmony_ci// If this FilePath contains a drive letter specification, returns the 436d528ed9Sopenharmony_ci// position of the last character of the drive letter specification, 446d528ed9Sopenharmony_ci// otherwise returns npos. This can only be true on Windows, when a pathname 456d528ed9Sopenharmony_ci// begins with a letter followed by a colon. On other platforms, this always 466d528ed9Sopenharmony_ci// returns npos. 476d528ed9Sopenharmony_ciStringViewType::size_type FindDriveLetter(StringViewType path) { 486d528ed9Sopenharmony_ci#if defined(FILE_PATH_USES_DRIVE_LETTERS) 496d528ed9Sopenharmony_ci // This is dependent on an ASCII-based character set, but that's a 506d528ed9Sopenharmony_ci // reasonable assumption. iswalpha can be too inclusive here. 516d528ed9Sopenharmony_ci if (path.length() >= 2 && path[1] == L':' && 526d528ed9Sopenharmony_ci ((path[0] >= L'A' && path[0] <= L'Z') || 536d528ed9Sopenharmony_ci (path[0] >= L'a' && path[0] <= L'z'))) { 546d528ed9Sopenharmony_ci return 1; 556d528ed9Sopenharmony_ci } 566d528ed9Sopenharmony_ci#endif // FILE_PATH_USES_DRIVE_LETTERS 576d528ed9Sopenharmony_ci return StringType::npos; 586d528ed9Sopenharmony_ci} 596d528ed9Sopenharmony_ci 606d528ed9Sopenharmony_ci#if defined(FILE_PATH_USES_DRIVE_LETTERS) 616d528ed9Sopenharmony_cibool EqualDriveLetterCaseInsensitive(StringViewType a, StringViewType b) { 626d528ed9Sopenharmony_ci size_t a_letter_pos = FindDriveLetter(a); 636d528ed9Sopenharmony_ci size_t b_letter_pos = FindDriveLetter(b); 646d528ed9Sopenharmony_ci 656d528ed9Sopenharmony_ci if (a_letter_pos == StringType::npos || b_letter_pos == StringType::npos) 666d528ed9Sopenharmony_ci return a == b; 676d528ed9Sopenharmony_ci 686d528ed9Sopenharmony_ci StringViewType a_letter(a.substr(0, a_letter_pos + 1)); 696d528ed9Sopenharmony_ci StringViewType b_letter(b.substr(0, b_letter_pos + 1)); 706d528ed9Sopenharmony_ci if (!StartsWithCaseInsensitiveASCII(a_letter, b_letter)) 716d528ed9Sopenharmony_ci return false; 726d528ed9Sopenharmony_ci 736d528ed9Sopenharmony_ci StringViewType a_rest(a.substr(a_letter_pos + 1)); 746d528ed9Sopenharmony_ci StringViewType b_rest(b.substr(b_letter_pos + 1)); 756d528ed9Sopenharmony_ci return a_rest == b_rest; 766d528ed9Sopenharmony_ci} 776d528ed9Sopenharmony_ci#endif // defined(FILE_PATH_USES_DRIVE_LETTERS) 786d528ed9Sopenharmony_ci 796d528ed9Sopenharmony_cibool IsPathAbsolute(StringViewType path) { 806d528ed9Sopenharmony_ci#if defined(FILE_PATH_USES_DRIVE_LETTERS) 816d528ed9Sopenharmony_ci StringType::size_type letter = FindDriveLetter(path); 826d528ed9Sopenharmony_ci if (letter != StringType::npos) { 836d528ed9Sopenharmony_ci // Look for a separator right after the drive specification. 846d528ed9Sopenharmony_ci return path.length() > letter + 1 && 856d528ed9Sopenharmony_ci FilePath::IsSeparator(path[letter + 1]); 866d528ed9Sopenharmony_ci } 876d528ed9Sopenharmony_ci // Look for a pair of leading separators. 886d528ed9Sopenharmony_ci return path.length() > 1 && FilePath::IsSeparator(path[0]) && 896d528ed9Sopenharmony_ci FilePath::IsSeparator(path[1]); 906d528ed9Sopenharmony_ci#else // FILE_PATH_USES_DRIVE_LETTERS 916d528ed9Sopenharmony_ci // Look for a separator in the first position. 926d528ed9Sopenharmony_ci return path.length() > 0 && FilePath::IsSeparator(path[0]); 936d528ed9Sopenharmony_ci#endif // FILE_PATH_USES_DRIVE_LETTERS 946d528ed9Sopenharmony_ci} 956d528ed9Sopenharmony_ci 966d528ed9Sopenharmony_cibool AreAllSeparators(const StringType& input) { 976d528ed9Sopenharmony_ci for (StringType::const_iterator it = input.begin(); it != input.end(); ++it) { 986d528ed9Sopenharmony_ci if (!FilePath::IsSeparator(*it)) 996d528ed9Sopenharmony_ci return false; 1006d528ed9Sopenharmony_ci } 1016d528ed9Sopenharmony_ci 1026d528ed9Sopenharmony_ci return true; 1036d528ed9Sopenharmony_ci} 1046d528ed9Sopenharmony_ci 1056d528ed9Sopenharmony_ci// Find the position of the '.' that separates the extension from the rest 1066d528ed9Sopenharmony_ci// of the file name. The position is relative to BaseName(), not value(). 1076d528ed9Sopenharmony_ci// Returns npos if it can't find an extension. 1086d528ed9Sopenharmony_ciStringType::size_type FinalExtensionSeparatorPosition(const StringType& path) { 1096d528ed9Sopenharmony_ci // Special case "." and ".." 1106d528ed9Sopenharmony_ci if (path == FilePath::kCurrentDirectory || path == FilePath::kParentDirectory) 1116d528ed9Sopenharmony_ci return StringType::npos; 1126d528ed9Sopenharmony_ci 1136d528ed9Sopenharmony_ci return path.rfind(FilePath::kExtensionSeparator); 1146d528ed9Sopenharmony_ci} 1156d528ed9Sopenharmony_ci 1166d528ed9Sopenharmony_ci// Same as above, but allow a second extension component of up to 4 1176d528ed9Sopenharmony_ci// characters when the rightmost extension component is a common double 1186d528ed9Sopenharmony_ci// extension (gz, bz2, Z). For example, foo.tar.gz or foo.tar.Z would have 1196d528ed9Sopenharmony_ci// extension components of '.tar.gz' and '.tar.Z' respectively. 1206d528ed9Sopenharmony_ciStringType::size_type ExtensionSeparatorPosition(const StringType& path) { 1216d528ed9Sopenharmony_ci const StringType::size_type last_dot = FinalExtensionSeparatorPosition(path); 1226d528ed9Sopenharmony_ci 1236d528ed9Sopenharmony_ci // No extension, or the extension is the whole filename. 1246d528ed9Sopenharmony_ci if (last_dot == StringType::npos || last_dot == 0U) 1256d528ed9Sopenharmony_ci return last_dot; 1266d528ed9Sopenharmony_ci 1276d528ed9Sopenharmony_ci const StringType::size_type penultimate_dot = 1286d528ed9Sopenharmony_ci path.rfind(FilePath::kExtensionSeparator, last_dot - 1); 1296d528ed9Sopenharmony_ci const StringType::size_type last_separator = path.find_last_of( 1306d528ed9Sopenharmony_ci FilePath::kSeparators, last_dot - 1, FilePath::kSeparatorsLength - 1); 1316d528ed9Sopenharmony_ci 1326d528ed9Sopenharmony_ci if (penultimate_dot == StringType::npos || 1336d528ed9Sopenharmony_ci (last_separator != StringType::npos && 1346d528ed9Sopenharmony_ci penultimate_dot < last_separator)) { 1356d528ed9Sopenharmony_ci return last_dot; 1366d528ed9Sopenharmony_ci } 1376d528ed9Sopenharmony_ci 1386d528ed9Sopenharmony_ci for (size_t i = 0; i < std::size(kCommonDoubleExtensions); ++i) { 1396d528ed9Sopenharmony_ci StringType extension(path, penultimate_dot + 1); 1406d528ed9Sopenharmony_ci if (LowerCaseEqualsASCII(extension, kCommonDoubleExtensions[i])) 1416d528ed9Sopenharmony_ci return penultimate_dot; 1426d528ed9Sopenharmony_ci } 1436d528ed9Sopenharmony_ci 1446d528ed9Sopenharmony_ci StringType extension(path, last_dot + 1); 1456d528ed9Sopenharmony_ci for (size_t i = 0; i < std::size(kCommonDoubleExtensionSuffixes); ++i) { 1466d528ed9Sopenharmony_ci if (LowerCaseEqualsASCII(extension, kCommonDoubleExtensionSuffixes[i])) { 1476d528ed9Sopenharmony_ci if ((last_dot - penultimate_dot) <= 5U && 1486d528ed9Sopenharmony_ci (last_dot - penultimate_dot) > 1U) { 1496d528ed9Sopenharmony_ci return penultimate_dot; 1506d528ed9Sopenharmony_ci } 1516d528ed9Sopenharmony_ci } 1526d528ed9Sopenharmony_ci } 1536d528ed9Sopenharmony_ci 1546d528ed9Sopenharmony_ci return last_dot; 1556d528ed9Sopenharmony_ci} 1566d528ed9Sopenharmony_ci 1576d528ed9Sopenharmony_ci// Returns true if path is "", ".", or "..". 1586d528ed9Sopenharmony_cibool IsEmptyOrSpecialCase(const StringType& path) { 1596d528ed9Sopenharmony_ci // Special cases "", ".", and ".." 1606d528ed9Sopenharmony_ci if (path.empty() || path == FilePath::kCurrentDirectory || 1616d528ed9Sopenharmony_ci path == FilePath::kParentDirectory) { 1626d528ed9Sopenharmony_ci return true; 1636d528ed9Sopenharmony_ci } 1646d528ed9Sopenharmony_ci 1656d528ed9Sopenharmony_ci return false; 1666d528ed9Sopenharmony_ci} 1676d528ed9Sopenharmony_ci 1686d528ed9Sopenharmony_ci} // namespace 1696d528ed9Sopenharmony_ci 1706d528ed9Sopenharmony_ciFilePath::FilePath() = default; 1716d528ed9Sopenharmony_ci 1726d528ed9Sopenharmony_ciFilePath::FilePath(const FilePath& that) = default; 1736d528ed9Sopenharmony_ciFilePath::FilePath(FilePath&& that) noexcept = default; 1746d528ed9Sopenharmony_ci 1756d528ed9Sopenharmony_ciFilePath::FilePath(StringViewType path) { 1766d528ed9Sopenharmony_ci path_.assign(path); 1776d528ed9Sopenharmony_ci StringType::size_type nul_pos = path_.find(kStringTerminator); 1786d528ed9Sopenharmony_ci if (nul_pos != StringType::npos) 1796d528ed9Sopenharmony_ci path_.erase(nul_pos, StringType::npos); 1806d528ed9Sopenharmony_ci} 1816d528ed9Sopenharmony_ci 1826d528ed9Sopenharmony_ciFilePath::~FilePath() = default; 1836d528ed9Sopenharmony_ci 1846d528ed9Sopenharmony_ciFilePath& FilePath::operator=(const FilePath& that) = default; 1856d528ed9Sopenharmony_ci 1866d528ed9Sopenharmony_ciFilePath& FilePath::operator=(FilePath&& that) = default; 1876d528ed9Sopenharmony_ci 1886d528ed9Sopenharmony_cibool FilePath::operator==(const FilePath& that) const { 1896d528ed9Sopenharmony_ci#if defined(FILE_PATH_USES_DRIVE_LETTERS) 1906d528ed9Sopenharmony_ci return EqualDriveLetterCaseInsensitive(this->path_, that.path_); 1916d528ed9Sopenharmony_ci#else // defined(FILE_PATH_USES_DRIVE_LETTERS) 1926d528ed9Sopenharmony_ci return path_ == that.path_; 1936d528ed9Sopenharmony_ci#endif // defined(FILE_PATH_USES_DRIVE_LETTERS) 1946d528ed9Sopenharmony_ci} 1956d528ed9Sopenharmony_ci 1966d528ed9Sopenharmony_cibool FilePath::operator!=(const FilePath& that) const { 1976d528ed9Sopenharmony_ci#if defined(FILE_PATH_USES_DRIVE_LETTERS) 1986d528ed9Sopenharmony_ci return !EqualDriveLetterCaseInsensitive(this->path_, that.path_); 1996d528ed9Sopenharmony_ci#else // defined(FILE_PATH_USES_DRIVE_LETTERS) 2006d528ed9Sopenharmony_ci return path_ != that.path_; 2016d528ed9Sopenharmony_ci#endif // defined(FILE_PATH_USES_DRIVE_LETTERS) 2026d528ed9Sopenharmony_ci} 2036d528ed9Sopenharmony_ci 2046d528ed9Sopenharmony_ci// static 2056d528ed9Sopenharmony_cibool FilePath::IsSeparator(CharType character) { 2066d528ed9Sopenharmony_ci for (size_t i = 0; i < kSeparatorsLength - 1; ++i) { 2076d528ed9Sopenharmony_ci if (character == kSeparators[i]) { 2086d528ed9Sopenharmony_ci return true; 2096d528ed9Sopenharmony_ci } 2106d528ed9Sopenharmony_ci } 2116d528ed9Sopenharmony_ci 2126d528ed9Sopenharmony_ci return false; 2136d528ed9Sopenharmony_ci} 2146d528ed9Sopenharmony_ci 2156d528ed9Sopenharmony_civoid FilePath::GetComponents(std::vector<StringType>* components) const { 2166d528ed9Sopenharmony_ci DCHECK(components); 2176d528ed9Sopenharmony_ci if (!components) 2186d528ed9Sopenharmony_ci return; 2196d528ed9Sopenharmony_ci components->clear(); 2206d528ed9Sopenharmony_ci if (value().empty()) 2216d528ed9Sopenharmony_ci return; 2226d528ed9Sopenharmony_ci 2236d528ed9Sopenharmony_ci std::vector<StringType> ret_val; 2246d528ed9Sopenharmony_ci FilePath current = *this; 2256d528ed9Sopenharmony_ci FilePath base; 2266d528ed9Sopenharmony_ci 2276d528ed9Sopenharmony_ci // Capture path components. 2286d528ed9Sopenharmony_ci while (current != current.DirName()) { 2296d528ed9Sopenharmony_ci base = current.BaseName(); 2306d528ed9Sopenharmony_ci if (!AreAllSeparators(base.value())) 2316d528ed9Sopenharmony_ci ret_val.push_back(base.value()); 2326d528ed9Sopenharmony_ci current = current.DirName(); 2336d528ed9Sopenharmony_ci } 2346d528ed9Sopenharmony_ci 2356d528ed9Sopenharmony_ci // Capture root, if any. 2366d528ed9Sopenharmony_ci base = current.BaseName(); 2376d528ed9Sopenharmony_ci if (!base.value().empty() && base.value() != kCurrentDirectory) 2386d528ed9Sopenharmony_ci ret_val.push_back(current.BaseName().value()); 2396d528ed9Sopenharmony_ci 2406d528ed9Sopenharmony_ci // Capture drive letter, if any. 2416d528ed9Sopenharmony_ci FilePath dir = current.DirName(); 2426d528ed9Sopenharmony_ci StringType::size_type letter = FindDriveLetter(dir.value()); 2436d528ed9Sopenharmony_ci if (letter != StringType::npos) { 2446d528ed9Sopenharmony_ci ret_val.push_back(StringType(dir.value(), 0, letter + 1)); 2456d528ed9Sopenharmony_ci } 2466d528ed9Sopenharmony_ci 2476d528ed9Sopenharmony_ci *components = std::vector<StringType>(ret_val.rbegin(), ret_val.rend()); 2486d528ed9Sopenharmony_ci} 2496d528ed9Sopenharmony_ci 2506d528ed9Sopenharmony_cibool FilePath::IsParent(const FilePath& child) const { 2516d528ed9Sopenharmony_ci return AppendRelativePath(child, nullptr); 2526d528ed9Sopenharmony_ci} 2536d528ed9Sopenharmony_ci 2546d528ed9Sopenharmony_cibool FilePath::AppendRelativePath(const FilePath& child, FilePath* path) const { 2556d528ed9Sopenharmony_ci std::vector<StringType> parent_components; 2566d528ed9Sopenharmony_ci std::vector<StringType> child_components; 2576d528ed9Sopenharmony_ci GetComponents(&parent_components); 2586d528ed9Sopenharmony_ci child.GetComponents(&child_components); 2596d528ed9Sopenharmony_ci 2606d528ed9Sopenharmony_ci if (parent_components.empty() || 2616d528ed9Sopenharmony_ci parent_components.size() >= child_components.size()) 2626d528ed9Sopenharmony_ci return false; 2636d528ed9Sopenharmony_ci 2646d528ed9Sopenharmony_ci std::vector<StringType>::const_iterator parent_comp = 2656d528ed9Sopenharmony_ci parent_components.begin(); 2666d528ed9Sopenharmony_ci std::vector<StringType>::const_iterator child_comp = child_components.begin(); 2676d528ed9Sopenharmony_ci 2686d528ed9Sopenharmony_ci#if defined(FILE_PATH_USES_DRIVE_LETTERS) 2696d528ed9Sopenharmony_ci // Windows can access case sensitive filesystems, so component 2706d528ed9Sopenharmony_ci // comparisons must be case sensitive, but drive letters are 2716d528ed9Sopenharmony_ci // never case sensitive. 2726d528ed9Sopenharmony_ci if ((FindDriveLetter(*parent_comp) != StringType::npos) && 2736d528ed9Sopenharmony_ci (FindDriveLetter(*child_comp) != StringType::npos)) { 2746d528ed9Sopenharmony_ci if (!StartsWithCaseInsensitiveASCII(*parent_comp, *child_comp)) 2756d528ed9Sopenharmony_ci return false; 2766d528ed9Sopenharmony_ci ++parent_comp; 2776d528ed9Sopenharmony_ci ++child_comp; 2786d528ed9Sopenharmony_ci } 2796d528ed9Sopenharmony_ci#endif // defined(FILE_PATH_USES_DRIVE_LETTERS) 2806d528ed9Sopenharmony_ci 2816d528ed9Sopenharmony_ci while (parent_comp != parent_components.end()) { 2826d528ed9Sopenharmony_ci if (*parent_comp != *child_comp) 2836d528ed9Sopenharmony_ci return false; 2846d528ed9Sopenharmony_ci ++parent_comp; 2856d528ed9Sopenharmony_ci ++child_comp; 2866d528ed9Sopenharmony_ci } 2876d528ed9Sopenharmony_ci 2886d528ed9Sopenharmony_ci if (path != nullptr) { 2896d528ed9Sopenharmony_ci for (; child_comp != child_components.end(); ++child_comp) { 2906d528ed9Sopenharmony_ci *path = path->Append(*child_comp); 2916d528ed9Sopenharmony_ci } 2926d528ed9Sopenharmony_ci } 2936d528ed9Sopenharmony_ci return true; 2946d528ed9Sopenharmony_ci} 2956d528ed9Sopenharmony_ci 2966d528ed9Sopenharmony_ci// libgen's dirname and basename aren't guaranteed to be thread-safe and aren't 2976d528ed9Sopenharmony_ci// guaranteed to not modify their input strings, and in fact are implemented 2986d528ed9Sopenharmony_ci// differently in this regard on different platforms. Don't use them, but 2996d528ed9Sopenharmony_ci// adhere to their behavior. 3006d528ed9Sopenharmony_ciFilePath FilePath::DirName() const { 3016d528ed9Sopenharmony_ci FilePath new_path(path_); 3026d528ed9Sopenharmony_ci new_path.StripTrailingSeparatorsInternal(); 3036d528ed9Sopenharmony_ci 3046d528ed9Sopenharmony_ci // The drive letter, if any, always needs to remain in the output. If there 3056d528ed9Sopenharmony_ci // is no drive letter, as will always be the case on platforms which do not 3066d528ed9Sopenharmony_ci // support drive letters, letter will be npos, or -1, so the comparisons and 3076d528ed9Sopenharmony_ci // resizes below using letter will still be valid. 3086d528ed9Sopenharmony_ci StringType::size_type letter = FindDriveLetter(new_path.path_); 3096d528ed9Sopenharmony_ci 3106d528ed9Sopenharmony_ci StringType::size_type last_separator = new_path.path_.find_last_of( 3116d528ed9Sopenharmony_ci kSeparators, StringType::npos, kSeparatorsLength - 1); 3126d528ed9Sopenharmony_ci if (last_separator == StringType::npos) { 3136d528ed9Sopenharmony_ci // path_ is in the current directory. 3146d528ed9Sopenharmony_ci new_path.path_.resize(letter + 1); 3156d528ed9Sopenharmony_ci } else if (last_separator == letter + 1) { 3166d528ed9Sopenharmony_ci // path_ is in the root directory. 3176d528ed9Sopenharmony_ci new_path.path_.resize(letter + 2); 3186d528ed9Sopenharmony_ci } else if (last_separator == letter + 2 && 3196d528ed9Sopenharmony_ci IsSeparator(new_path.path_[letter + 1])) { 3206d528ed9Sopenharmony_ci // path_ is in "//" (possibly with a drive letter); leave the double 3216d528ed9Sopenharmony_ci // separator intact indicating alternate root. 3226d528ed9Sopenharmony_ci new_path.path_.resize(letter + 3); 3236d528ed9Sopenharmony_ci } else if (last_separator != 0) { 3246d528ed9Sopenharmony_ci // path_ is somewhere else, trim the basename. 3256d528ed9Sopenharmony_ci new_path.path_.resize(last_separator); 3266d528ed9Sopenharmony_ci } 3276d528ed9Sopenharmony_ci 3286d528ed9Sopenharmony_ci new_path.StripTrailingSeparatorsInternal(); 3296d528ed9Sopenharmony_ci if (!new_path.path_.length()) 3306d528ed9Sopenharmony_ci new_path.path_ = kCurrentDirectory; 3316d528ed9Sopenharmony_ci 3326d528ed9Sopenharmony_ci return new_path; 3336d528ed9Sopenharmony_ci} 3346d528ed9Sopenharmony_ci 3356d528ed9Sopenharmony_ciFilePath FilePath::BaseName() const { 3366d528ed9Sopenharmony_ci FilePath new_path(path_); 3376d528ed9Sopenharmony_ci new_path.StripTrailingSeparatorsInternal(); 3386d528ed9Sopenharmony_ci 3396d528ed9Sopenharmony_ci // The drive letter, if any, is always stripped. 3406d528ed9Sopenharmony_ci StringType::size_type letter = FindDriveLetter(new_path.path_); 3416d528ed9Sopenharmony_ci if (letter != StringType::npos) { 3426d528ed9Sopenharmony_ci new_path.path_.erase(0, letter + 1); 3436d528ed9Sopenharmony_ci } 3446d528ed9Sopenharmony_ci 3456d528ed9Sopenharmony_ci // Keep everything after the final separator, but if the pathname is only 3466d528ed9Sopenharmony_ci // one character and it's a separator, leave it alone. 3476d528ed9Sopenharmony_ci StringType::size_type last_separator = new_path.path_.find_last_of( 3486d528ed9Sopenharmony_ci kSeparators, StringType::npos, kSeparatorsLength - 1); 3496d528ed9Sopenharmony_ci if (last_separator != StringType::npos && 3506d528ed9Sopenharmony_ci last_separator < new_path.path_.length() - 1) { 3516d528ed9Sopenharmony_ci new_path.path_.erase(0, last_separator + 1); 3526d528ed9Sopenharmony_ci } 3536d528ed9Sopenharmony_ci 3546d528ed9Sopenharmony_ci return new_path; 3556d528ed9Sopenharmony_ci} 3566d528ed9Sopenharmony_ci 3576d528ed9Sopenharmony_ciStringType FilePath::Extension() const { 3586d528ed9Sopenharmony_ci FilePath base(BaseName()); 3596d528ed9Sopenharmony_ci const StringType::size_type dot = ExtensionSeparatorPosition(base.path_); 3606d528ed9Sopenharmony_ci if (dot == StringType::npos) 3616d528ed9Sopenharmony_ci return StringType(); 3626d528ed9Sopenharmony_ci 3636d528ed9Sopenharmony_ci return base.path_.substr(dot, StringType::npos); 3646d528ed9Sopenharmony_ci} 3656d528ed9Sopenharmony_ci 3666d528ed9Sopenharmony_ciStringType FilePath::FinalExtension() const { 3676d528ed9Sopenharmony_ci FilePath base(BaseName()); 3686d528ed9Sopenharmony_ci const StringType::size_type dot = FinalExtensionSeparatorPosition(base.path_); 3696d528ed9Sopenharmony_ci if (dot == StringType::npos) 3706d528ed9Sopenharmony_ci return StringType(); 3716d528ed9Sopenharmony_ci 3726d528ed9Sopenharmony_ci return base.path_.substr(dot, StringType::npos); 3736d528ed9Sopenharmony_ci} 3746d528ed9Sopenharmony_ci 3756d528ed9Sopenharmony_ciFilePath FilePath::RemoveExtension() const { 3766d528ed9Sopenharmony_ci if (Extension().empty()) 3776d528ed9Sopenharmony_ci return *this; 3786d528ed9Sopenharmony_ci 3796d528ed9Sopenharmony_ci const StringType::size_type dot = ExtensionSeparatorPosition(path_); 3806d528ed9Sopenharmony_ci if (dot == StringType::npos) 3816d528ed9Sopenharmony_ci return *this; 3826d528ed9Sopenharmony_ci 3836d528ed9Sopenharmony_ci return FilePath(path_.substr(0, dot)); 3846d528ed9Sopenharmony_ci} 3856d528ed9Sopenharmony_ci 3866d528ed9Sopenharmony_ciFilePath FilePath::RemoveFinalExtension() const { 3876d528ed9Sopenharmony_ci if (FinalExtension().empty()) 3886d528ed9Sopenharmony_ci return *this; 3896d528ed9Sopenharmony_ci 3906d528ed9Sopenharmony_ci const StringType::size_type dot = FinalExtensionSeparatorPosition(path_); 3916d528ed9Sopenharmony_ci if (dot == StringType::npos) 3926d528ed9Sopenharmony_ci return *this; 3936d528ed9Sopenharmony_ci 3946d528ed9Sopenharmony_ci return FilePath(path_.substr(0, dot)); 3956d528ed9Sopenharmony_ci} 3966d528ed9Sopenharmony_ci 3976d528ed9Sopenharmony_ciFilePath FilePath::InsertBeforeExtension(StringViewType suffix) const { 3986d528ed9Sopenharmony_ci if (suffix.empty()) 3996d528ed9Sopenharmony_ci return FilePath(path_); 4006d528ed9Sopenharmony_ci 4016d528ed9Sopenharmony_ci if (IsEmptyOrSpecialCase(BaseName().value())) 4026d528ed9Sopenharmony_ci return FilePath(); 4036d528ed9Sopenharmony_ci 4046d528ed9Sopenharmony_ci StringType ext = Extension(); 4056d528ed9Sopenharmony_ci StringType ret = RemoveExtension().value(); 4066d528ed9Sopenharmony_ci ret.append(suffix); 4076d528ed9Sopenharmony_ci ret.append(ext); 4086d528ed9Sopenharmony_ci return FilePath(ret); 4096d528ed9Sopenharmony_ci} 4106d528ed9Sopenharmony_ci 4116d528ed9Sopenharmony_ciFilePath FilePath::InsertBeforeExtensionASCII(std::string_view suffix) const { 4126d528ed9Sopenharmony_ci DCHECK(IsStringASCII(suffix)); 4136d528ed9Sopenharmony_ci#if defined(OS_WIN) 4146d528ed9Sopenharmony_ci return InsertBeforeExtension(ASCIIToUTF16(suffix)); 4156d528ed9Sopenharmony_ci#elif defined(OS_POSIX) || defined(OS_FUCHSIA) 4166d528ed9Sopenharmony_ci return InsertBeforeExtension(suffix); 4176d528ed9Sopenharmony_ci#endif 4186d528ed9Sopenharmony_ci} 4196d528ed9Sopenharmony_ci 4206d528ed9Sopenharmony_ciFilePath FilePath::AddExtension(StringViewType extension) const { 4216d528ed9Sopenharmony_ci if (IsEmptyOrSpecialCase(BaseName().value())) 4226d528ed9Sopenharmony_ci return FilePath(); 4236d528ed9Sopenharmony_ci 4246d528ed9Sopenharmony_ci // If the new extension is "" or ".", then just return the current FilePath. 4256d528ed9Sopenharmony_ci if (extension.empty() || 4266d528ed9Sopenharmony_ci (extension.size() == 1 && extension[0] == kExtensionSeparator)) 4276d528ed9Sopenharmony_ci return *this; 4286d528ed9Sopenharmony_ci 4296d528ed9Sopenharmony_ci StringType str = path_; 4306d528ed9Sopenharmony_ci if (extension[0] != kExtensionSeparator && 4316d528ed9Sopenharmony_ci *(str.end() - 1) != kExtensionSeparator) { 4326d528ed9Sopenharmony_ci str.append(1, kExtensionSeparator); 4336d528ed9Sopenharmony_ci } 4346d528ed9Sopenharmony_ci str.append(extension); 4356d528ed9Sopenharmony_ci return FilePath(str); 4366d528ed9Sopenharmony_ci} 4376d528ed9Sopenharmony_ci 4386d528ed9Sopenharmony_ciFilePath FilePath::ReplaceExtension(StringViewType extension) const { 4396d528ed9Sopenharmony_ci if (IsEmptyOrSpecialCase(BaseName().value())) 4406d528ed9Sopenharmony_ci return FilePath(); 4416d528ed9Sopenharmony_ci 4426d528ed9Sopenharmony_ci FilePath no_ext = RemoveExtension(); 4436d528ed9Sopenharmony_ci // If the new extension is "" or ".", then just remove the current extension. 4446d528ed9Sopenharmony_ci if (extension.empty() || 4456d528ed9Sopenharmony_ci (extension.size() == 1 && extension[0] == kExtensionSeparator)) 4466d528ed9Sopenharmony_ci return no_ext; 4476d528ed9Sopenharmony_ci 4486d528ed9Sopenharmony_ci StringType str = no_ext.value(); 4496d528ed9Sopenharmony_ci if (extension[0] != kExtensionSeparator) 4506d528ed9Sopenharmony_ci str.append(1, kExtensionSeparator); 4516d528ed9Sopenharmony_ci str.append(extension); 4526d528ed9Sopenharmony_ci return FilePath(str); 4536d528ed9Sopenharmony_ci} 4546d528ed9Sopenharmony_ci 4556d528ed9Sopenharmony_ciFilePath FilePath::Append(StringViewType component) const { 4566d528ed9Sopenharmony_ci StringViewType appended = component; 4576d528ed9Sopenharmony_ci StringType without_nuls; 4586d528ed9Sopenharmony_ci 4596d528ed9Sopenharmony_ci StringType::size_type nul_pos = component.find(kStringTerminator); 4606d528ed9Sopenharmony_ci if (nul_pos != StringViewType::npos) { 4616d528ed9Sopenharmony_ci without_nuls.assign(component.substr(0, nul_pos)); 4626d528ed9Sopenharmony_ci appended = StringViewType(without_nuls); 4636d528ed9Sopenharmony_ci } 4646d528ed9Sopenharmony_ci 4656d528ed9Sopenharmony_ci DCHECK(!IsPathAbsolute(appended)); 4666d528ed9Sopenharmony_ci 4676d528ed9Sopenharmony_ci if (path_.compare(kCurrentDirectory) == 0 && !appended.empty()) { 4686d528ed9Sopenharmony_ci // Append normally doesn't do any normalization, but as a special case, 4696d528ed9Sopenharmony_ci // when appending to kCurrentDirectory, just return a new path for the 4706d528ed9Sopenharmony_ci // component argument. Appending component to kCurrentDirectory would 4716d528ed9Sopenharmony_ci // serve no purpose other than needlessly lengthening the path, and 4726d528ed9Sopenharmony_ci // it's likely in practice to wind up with FilePath objects containing 4736d528ed9Sopenharmony_ci // only kCurrentDirectory when calling DirName on a single relative path 4746d528ed9Sopenharmony_ci // component. 4756d528ed9Sopenharmony_ci return FilePath(appended); 4766d528ed9Sopenharmony_ci } 4776d528ed9Sopenharmony_ci 4786d528ed9Sopenharmony_ci FilePath new_path(path_); 4796d528ed9Sopenharmony_ci new_path.StripTrailingSeparatorsInternal(); 4806d528ed9Sopenharmony_ci 4816d528ed9Sopenharmony_ci // Don't append a separator if the path is empty (indicating the current 4826d528ed9Sopenharmony_ci // directory) or if the path component is empty (indicating nothing to 4836d528ed9Sopenharmony_ci // append). 4846d528ed9Sopenharmony_ci if (!appended.empty() && !new_path.path_.empty()) { 4856d528ed9Sopenharmony_ci // Don't append a separator if the path still ends with a trailing 4866d528ed9Sopenharmony_ci // separator after stripping (indicating the root directory). 4876d528ed9Sopenharmony_ci if (!IsSeparator(new_path.path_.back())) { 4886d528ed9Sopenharmony_ci // Don't append a separator if the path is just a drive letter. 4896d528ed9Sopenharmony_ci if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) { 4906d528ed9Sopenharmony_ci new_path.path_.append(1, kSeparators[0]); 4916d528ed9Sopenharmony_ci } 4926d528ed9Sopenharmony_ci } 4936d528ed9Sopenharmony_ci } 4946d528ed9Sopenharmony_ci 4956d528ed9Sopenharmony_ci new_path.path_.append(appended); 4966d528ed9Sopenharmony_ci return new_path; 4976d528ed9Sopenharmony_ci} 4986d528ed9Sopenharmony_ci 4996d528ed9Sopenharmony_ciFilePath FilePath::Append(const FilePath& component) const { 5006d528ed9Sopenharmony_ci return Append(component.value()); 5016d528ed9Sopenharmony_ci} 5026d528ed9Sopenharmony_ci 5036d528ed9Sopenharmony_ciFilePath FilePath::AppendASCII(std::string_view component) const { 5046d528ed9Sopenharmony_ci DCHECK(base::IsStringASCII(component)); 5056d528ed9Sopenharmony_ci#if defined(OS_WIN) 5066d528ed9Sopenharmony_ci return Append(ASCIIToUTF16(component)); 5076d528ed9Sopenharmony_ci#elif defined(OS_POSIX) || defined(OS_FUCHSIA) 5086d528ed9Sopenharmony_ci return Append(component); 5096d528ed9Sopenharmony_ci#endif 5106d528ed9Sopenharmony_ci} 5116d528ed9Sopenharmony_ci 5126d528ed9Sopenharmony_cibool FilePath::IsAbsolute() const { 5136d528ed9Sopenharmony_ci return IsPathAbsolute(path_); 5146d528ed9Sopenharmony_ci} 5156d528ed9Sopenharmony_ci 5166d528ed9Sopenharmony_cibool FilePath::EndsWithSeparator() const { 5176d528ed9Sopenharmony_ci if (empty()) 5186d528ed9Sopenharmony_ci return false; 5196d528ed9Sopenharmony_ci return IsSeparator(path_.back()); 5206d528ed9Sopenharmony_ci} 5216d528ed9Sopenharmony_ci 5226d528ed9Sopenharmony_ciFilePath FilePath::AsEndingWithSeparator() const { 5236d528ed9Sopenharmony_ci if (EndsWithSeparator() || path_.empty()) 5246d528ed9Sopenharmony_ci return *this; 5256d528ed9Sopenharmony_ci 5266d528ed9Sopenharmony_ci StringType path_str; 5276d528ed9Sopenharmony_ci path_str.reserve(path_.length() + 1); // Only allocate string once. 5286d528ed9Sopenharmony_ci 5296d528ed9Sopenharmony_ci path_str = path_; 5306d528ed9Sopenharmony_ci path_str.append(&kSeparators[0], 1); 5316d528ed9Sopenharmony_ci return FilePath(path_str); 5326d528ed9Sopenharmony_ci} 5336d528ed9Sopenharmony_ci 5346d528ed9Sopenharmony_ciFilePath FilePath::StripTrailingSeparators() const { 5356d528ed9Sopenharmony_ci FilePath new_path(path_); 5366d528ed9Sopenharmony_ci new_path.StripTrailingSeparatorsInternal(); 5376d528ed9Sopenharmony_ci 5386d528ed9Sopenharmony_ci return new_path; 5396d528ed9Sopenharmony_ci} 5406d528ed9Sopenharmony_ci 5416d528ed9Sopenharmony_cibool FilePath::ReferencesParent() const { 5426d528ed9Sopenharmony_ci if (path_.find(kParentDirectory) == StringType::npos) { 5436d528ed9Sopenharmony_ci // GetComponents is quite expensive, so avoid calling it in the majority 5446d528ed9Sopenharmony_ci // of cases where there isn't a kParentDirectory anywhere in the path. 5456d528ed9Sopenharmony_ci return false; 5466d528ed9Sopenharmony_ci } 5476d528ed9Sopenharmony_ci 5486d528ed9Sopenharmony_ci std::vector<StringType> components; 5496d528ed9Sopenharmony_ci GetComponents(&components); 5506d528ed9Sopenharmony_ci 5516d528ed9Sopenharmony_ci std::vector<StringType>::const_iterator it = components.begin(); 5526d528ed9Sopenharmony_ci for (; it != components.end(); ++it) { 5536d528ed9Sopenharmony_ci const StringType& component = *it; 5546d528ed9Sopenharmony_ci // Windows has odd, undocumented behavior with path components containing 5556d528ed9Sopenharmony_ci // only whitespace and . characters. So, if all we see is . and 5566d528ed9Sopenharmony_ci // whitespace, then we treat any .. sequence as referencing parent. 5576d528ed9Sopenharmony_ci // For simplicity we enforce this on all platforms. 5586d528ed9Sopenharmony_ci if (component.find_first_not_of(FILE_PATH_LITERAL(". \n\r\t")) == 5596d528ed9Sopenharmony_ci std::string::npos && 5606d528ed9Sopenharmony_ci component.find(kParentDirectory) != std::string::npos) { 5616d528ed9Sopenharmony_ci return true; 5626d528ed9Sopenharmony_ci } 5636d528ed9Sopenharmony_ci } 5646d528ed9Sopenharmony_ci return false; 5656d528ed9Sopenharmony_ci} 5666d528ed9Sopenharmony_ci 5676d528ed9Sopenharmony_ci#if defined(OS_WIN) 5686d528ed9Sopenharmony_ci 5696d528ed9Sopenharmony_cistd::u16string FilePath::LossyDisplayName() const { 5706d528ed9Sopenharmony_ci return path_; 5716d528ed9Sopenharmony_ci} 5726d528ed9Sopenharmony_ci 5736d528ed9Sopenharmony_cistd::string FilePath::MaybeAsASCII() const { 5746d528ed9Sopenharmony_ci if (base::IsStringASCII(path_)) 5756d528ed9Sopenharmony_ci return UTF16ToASCII(path_); 5766d528ed9Sopenharmony_ci return std::string(); 5776d528ed9Sopenharmony_ci} 5786d528ed9Sopenharmony_ci 5796d528ed9Sopenharmony_cistd::string FilePath::As8Bit() const { 5806d528ed9Sopenharmony_ci return UTF16ToUTF8(value()); 5816d528ed9Sopenharmony_ci} 5826d528ed9Sopenharmony_ci 5836d528ed9Sopenharmony_ci#elif defined(OS_POSIX) || defined(OS_FUCHSIA) 5846d528ed9Sopenharmony_ci 5856d528ed9Sopenharmony_ci// See file_path.h for a discussion of the encoding of paths on POSIX 5866d528ed9Sopenharmony_ci// platforms. These encoding conversion functions are not quite correct. 5876d528ed9Sopenharmony_ci 5886d528ed9Sopenharmony_cistd::string FilePath::MaybeAsASCII() const { 5896d528ed9Sopenharmony_ci if (base::IsStringASCII(path_)) 5906d528ed9Sopenharmony_ci return path_; 5916d528ed9Sopenharmony_ci return std::string(); 5926d528ed9Sopenharmony_ci} 5936d528ed9Sopenharmony_ci 5946d528ed9Sopenharmony_cistd::string FilePath::As8Bit() const { 5956d528ed9Sopenharmony_ci return value(); 5966d528ed9Sopenharmony_ci} 5976d528ed9Sopenharmony_ci 5986d528ed9Sopenharmony_ci#endif // defined(OS_WIN) 5996d528ed9Sopenharmony_ci 6006d528ed9Sopenharmony_civoid FilePath::StripTrailingSeparatorsInternal() { 6016d528ed9Sopenharmony_ci // If there is no drive letter, start will be 1, which will prevent stripping 6026d528ed9Sopenharmony_ci // the leading separator if there is only one separator. If there is a drive 6036d528ed9Sopenharmony_ci // letter, start will be set appropriately to prevent stripping the first 6046d528ed9Sopenharmony_ci // separator following the drive letter, if a separator immediately follows 6056d528ed9Sopenharmony_ci // the drive letter. 6066d528ed9Sopenharmony_ci StringType::size_type start = FindDriveLetter(path_) + 2; 6076d528ed9Sopenharmony_ci 6086d528ed9Sopenharmony_ci StringType::size_type last_stripped = StringType::npos; 6096d528ed9Sopenharmony_ci for (StringType::size_type pos = path_.length(); 6106d528ed9Sopenharmony_ci pos > start && IsSeparator(path_[pos - 1]); --pos) { 6116d528ed9Sopenharmony_ci // If the string only has two separators and they're at the beginning, 6126d528ed9Sopenharmony_ci // don't strip them, unless the string began with more than two separators. 6136d528ed9Sopenharmony_ci if (pos != start + 1 || last_stripped == start + 2 || 6146d528ed9Sopenharmony_ci !IsSeparator(path_[start - 1])) { 6156d528ed9Sopenharmony_ci path_.resize(pos - 1); 6166d528ed9Sopenharmony_ci last_stripped = pos; 6176d528ed9Sopenharmony_ci } 6186d528ed9Sopenharmony_ci } 6196d528ed9Sopenharmony_ci} 6206d528ed9Sopenharmony_ci 6216d528ed9Sopenharmony_ciFilePath FilePath::NormalizePathSeparators() const { 6226d528ed9Sopenharmony_ci return NormalizePathSeparatorsTo(kSeparators[0]); 6236d528ed9Sopenharmony_ci} 6246d528ed9Sopenharmony_ci 6256d528ed9Sopenharmony_ciFilePath FilePath::NormalizePathSeparatorsTo(CharType separator) const { 6266d528ed9Sopenharmony_ci#if defined(FILE_PATH_USES_WIN_SEPARATORS) 6276d528ed9Sopenharmony_ci DCHECK_NE(static_cast<const void*>(kSeparators + kSeparatorsLength), 6286d528ed9Sopenharmony_ci static_cast<const void*>(std::find( 6296d528ed9Sopenharmony_ci kSeparators, kSeparators + kSeparatorsLength, separator))); 6306d528ed9Sopenharmony_ci StringType copy = path_; 6316d528ed9Sopenharmony_ci for (size_t i = 0; i < kSeparatorsLength; ++i) { 6326d528ed9Sopenharmony_ci std::replace(copy.begin(), copy.end(), kSeparators[i], separator); 6336d528ed9Sopenharmony_ci } 6346d528ed9Sopenharmony_ci return FilePath(copy); 6356d528ed9Sopenharmony_ci#else 6366d528ed9Sopenharmony_ci return *this; 6376d528ed9Sopenharmony_ci#endif 6386d528ed9Sopenharmony_ci} 6396d528ed9Sopenharmony_ci 6406d528ed9Sopenharmony_ci} // namespace base 641