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