| // 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 "base/files/file_path.h" | 
 |  | 
 | #include <string.h> | 
 |  | 
 | #include <algorithm> | 
 | #include <iterator> | 
 | #include <string_view> | 
 |  | 
 | #include "base/logging.h" | 
 | #include "base/macros.h" | 
 | #include "base/strings/string_util.h" | 
 | #include "base/strings/utf_string_conversions.h" | 
 | #include "util/build_config.h" | 
 |  | 
 | #if defined(OS_MACOSX) | 
 | #include "base/mac/scoped_cftyperef.h" | 
 | #include "base/third_party/icu/icu_utf.h" | 
 | #endif | 
 |  | 
 | #if defined(OS_WIN) | 
 | #include <windows.h> | 
 | #elif defined(OS_MACOSX) | 
 | #include <CoreFoundation/CoreFoundation.h> | 
 | #endif | 
 |  | 
 | namespace base { | 
 |  | 
 | using StringType = FilePath::StringType; | 
 | using StringViewType = FilePath::StringViewType; | 
 |  | 
 | namespace { | 
 |  | 
 | const char* const kCommonDoubleExtensionSuffixes[] = {"gz", "z", "bz2", "bz"}; | 
 | const char* const kCommonDoubleExtensions[] = {"user.js"}; | 
 |  | 
 | const FilePath::CharType kStringTerminator = FILE_PATH_LITERAL('\0'); | 
 |  | 
 | // If this FilePath contains a drive letter specification, returns the | 
 | // position of the last character of the drive letter specification, | 
 | // otherwise returns npos.  This can only be true on Windows, when a pathname | 
 | // begins with a letter followed by a colon.  On other platforms, this always | 
 | // returns npos. | 
 | StringViewType::size_type FindDriveLetter(StringViewType path) { | 
 | #if defined(FILE_PATH_USES_DRIVE_LETTERS) | 
 |   // This is dependent on an ASCII-based character set, but that's a | 
 |   // reasonable assumption.  iswalpha can be too inclusive here. | 
 |   if (path.length() >= 2 && path[1] == L':' && | 
 |       ((path[0] >= L'A' && path[0] <= L'Z') || | 
 |        (path[0] >= L'a' && path[0] <= L'z'))) { | 
 |     return 1; | 
 |   } | 
 | #endif  // FILE_PATH_USES_DRIVE_LETTERS | 
 |   return StringType::npos; | 
 | } | 
 |  | 
 | #if defined(FILE_PATH_USES_DRIVE_LETTERS) | 
 | bool EqualDriveLetterCaseInsensitive(StringViewType a, StringViewType b) { | 
 |   size_t a_letter_pos = FindDriveLetter(a); | 
 |   size_t b_letter_pos = FindDriveLetter(b); | 
 |  | 
 |   if (a_letter_pos == StringType::npos || b_letter_pos == StringType::npos) | 
 |     return a == b; | 
 |  | 
 |   StringViewType a_letter(a.substr(0, a_letter_pos + 1)); | 
 |   StringViewType b_letter(b.substr(0, b_letter_pos + 1)); | 
 |   if (!StartsWith(a_letter, b_letter, CompareCase::INSENSITIVE_ASCII)) | 
 |     return false; | 
 |  | 
 |   StringViewType a_rest(a.substr(a_letter_pos + 1)); | 
 |   StringViewType b_rest(b.substr(b_letter_pos + 1)); | 
 |   return a_rest == b_rest; | 
 | } | 
 | #endif  // defined(FILE_PATH_USES_DRIVE_LETTERS) | 
 |  | 
 | bool IsPathAbsolute(StringViewType path) { | 
 | #if defined(FILE_PATH_USES_DRIVE_LETTERS) | 
 |   StringType::size_type letter = FindDriveLetter(path); | 
 |   if (letter != StringType::npos) { | 
 |     // Look for a separator right after the drive specification. | 
 |     return path.length() > letter + 1 && | 
 |            FilePath::IsSeparator(path[letter + 1]); | 
 |   } | 
 |   // Look for a pair of leading separators. | 
 |   return path.length() > 1 && FilePath::IsSeparator(path[0]) && | 
 |          FilePath::IsSeparator(path[1]); | 
 | #else   // FILE_PATH_USES_DRIVE_LETTERS | 
 |   // Look for a separator in the first position. | 
 |   return path.length() > 0 && FilePath::IsSeparator(path[0]); | 
 | #endif  // FILE_PATH_USES_DRIVE_LETTERS | 
 | } | 
 |  | 
 | bool AreAllSeparators(const StringType& input) { | 
 |   for (StringType::const_iterator it = input.begin(); it != input.end(); ++it) { | 
 |     if (!FilePath::IsSeparator(*it)) | 
 |       return false; | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | // Find the position of the '.' that separates the extension from the rest | 
 | // of the file name. The position is relative to BaseName(), not value(). | 
 | // Returns npos if it can't find an extension. | 
 | StringType::size_type FinalExtensionSeparatorPosition(const StringType& path) { | 
 |   // Special case "." and ".." | 
 |   if (path == FilePath::kCurrentDirectory || path == FilePath::kParentDirectory) | 
 |     return StringType::npos; | 
 |  | 
 |   return path.rfind(FilePath::kExtensionSeparator); | 
 | } | 
 |  | 
 | // Same as above, but allow a second extension component of up to 4 | 
 | // characters when the rightmost extension component is a common double | 
 | // extension (gz, bz2, Z).  For example, foo.tar.gz or foo.tar.Z would have | 
 | // extension components of '.tar.gz' and '.tar.Z' respectively. | 
 | StringType::size_type ExtensionSeparatorPosition(const StringType& path) { | 
 |   const StringType::size_type last_dot = FinalExtensionSeparatorPosition(path); | 
 |  | 
 |   // No extension, or the extension is the whole filename. | 
 |   if (last_dot == StringType::npos || last_dot == 0U) | 
 |     return last_dot; | 
 |  | 
 |   const StringType::size_type penultimate_dot = | 
 |       path.rfind(FilePath::kExtensionSeparator, last_dot - 1); | 
 |   const StringType::size_type last_separator = path.find_last_of( | 
 |       FilePath::kSeparators, last_dot - 1, FilePath::kSeparatorsLength - 1); | 
 |  | 
 |   if (penultimate_dot == StringType::npos || | 
 |       (last_separator != StringType::npos && | 
 |        penultimate_dot < last_separator)) { | 
 |     return last_dot; | 
 |   } | 
 |  | 
 |   for (size_t i = 0; i < std::size(kCommonDoubleExtensions); ++i) { | 
 |     StringType extension(path, penultimate_dot + 1); | 
 |     if (LowerCaseEqualsASCII(extension, kCommonDoubleExtensions[i])) | 
 |       return penultimate_dot; | 
 |   } | 
 |  | 
 |   StringType extension(path, last_dot + 1); | 
 |   for (size_t i = 0; i < std::size(kCommonDoubleExtensionSuffixes); ++i) { | 
 |     if (LowerCaseEqualsASCII(extension, kCommonDoubleExtensionSuffixes[i])) { | 
 |       if ((last_dot - penultimate_dot) <= 5U && | 
 |           (last_dot - penultimate_dot) > 1U) { | 
 |         return penultimate_dot; | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   return last_dot; | 
 | } | 
 |  | 
 | // Returns true if path is "", ".", or "..". | 
 | bool IsEmptyOrSpecialCase(const StringType& path) { | 
 |   // Special cases "", ".", and ".." | 
 |   if (path.empty() || path == FilePath::kCurrentDirectory || | 
 |       path == FilePath::kParentDirectory) { | 
 |     return true; | 
 |   } | 
 |  | 
 |   return false; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | FilePath::FilePath() = default; | 
 |  | 
 | FilePath::FilePath(const FilePath& that) = default; | 
 | FilePath::FilePath(FilePath&& that) noexcept = default; | 
 |  | 
 | FilePath::FilePath(StringViewType path) { | 
 |   path_.assign(path); | 
 |   StringType::size_type nul_pos = path_.find(kStringTerminator); | 
 |   if (nul_pos != StringType::npos) | 
 |     path_.erase(nul_pos, StringType::npos); | 
 | } | 
 |  | 
 | FilePath::~FilePath() = default; | 
 |  | 
 | FilePath& FilePath::operator=(const FilePath& that) = default; | 
 |  | 
 | FilePath& FilePath::operator=(FilePath&& that) = default; | 
 |  | 
 | bool FilePath::operator==(const FilePath& that) const { | 
 | #if defined(FILE_PATH_USES_DRIVE_LETTERS) | 
 |   return EqualDriveLetterCaseInsensitive(this->path_, that.path_); | 
 | #else   // defined(FILE_PATH_USES_DRIVE_LETTERS) | 
 |   return path_ == that.path_; | 
 | #endif  // defined(FILE_PATH_USES_DRIVE_LETTERS) | 
 | } | 
 |  | 
 | bool FilePath::operator!=(const FilePath& that) const { | 
 | #if defined(FILE_PATH_USES_DRIVE_LETTERS) | 
 |   return !EqualDriveLetterCaseInsensitive(this->path_, that.path_); | 
 | #else   // defined(FILE_PATH_USES_DRIVE_LETTERS) | 
 |   return path_ != that.path_; | 
 | #endif  // defined(FILE_PATH_USES_DRIVE_LETTERS) | 
 | } | 
 |  | 
 | // static | 
 | bool FilePath::IsSeparator(CharType character) { | 
 |   for (size_t i = 0; i < kSeparatorsLength - 1; ++i) { | 
 |     if (character == kSeparators[i]) { | 
 |       return true; | 
 |     } | 
 |   } | 
 |  | 
 |   return false; | 
 | } | 
 |  | 
 | void FilePath::GetComponents(std::vector<StringType>* components) const { | 
 |   DCHECK(components); | 
 |   if (!components) | 
 |     return; | 
 |   components->clear(); | 
 |   if (value().empty()) | 
 |     return; | 
 |  | 
 |   std::vector<StringType> ret_val; | 
 |   FilePath current = *this; | 
 |   FilePath base; | 
 |  | 
 |   // Capture path components. | 
 |   while (current != current.DirName()) { | 
 |     base = current.BaseName(); | 
 |     if (!AreAllSeparators(base.value())) | 
 |       ret_val.push_back(base.value()); | 
 |     current = current.DirName(); | 
 |   } | 
 |  | 
 |   // Capture root, if any. | 
 |   base = current.BaseName(); | 
 |   if (!base.value().empty() && base.value() != kCurrentDirectory) | 
 |     ret_val.push_back(current.BaseName().value()); | 
 |  | 
 |   // Capture drive letter, if any. | 
 |   FilePath dir = current.DirName(); | 
 |   StringType::size_type letter = FindDriveLetter(dir.value()); | 
 |   if (letter != StringType::npos) { | 
 |     ret_val.push_back(StringType(dir.value(), 0, letter + 1)); | 
 |   } | 
 |  | 
 |   *components = std::vector<StringType>(ret_val.rbegin(), ret_val.rend()); | 
 | } | 
 |  | 
 | bool FilePath::IsParent(const FilePath& child) const { | 
 |   return AppendRelativePath(child, nullptr); | 
 | } | 
 |  | 
 | bool FilePath::AppendRelativePath(const FilePath& child, FilePath* path) const { | 
 |   std::vector<StringType> parent_components; | 
 |   std::vector<StringType> child_components; | 
 |   GetComponents(&parent_components); | 
 |   child.GetComponents(&child_components); | 
 |  | 
 |   if (parent_components.empty() || | 
 |       parent_components.size() >= child_components.size()) | 
 |     return false; | 
 |  | 
 |   std::vector<StringType>::const_iterator parent_comp = | 
 |       parent_components.begin(); | 
 |   std::vector<StringType>::const_iterator child_comp = child_components.begin(); | 
 |  | 
 | #if defined(FILE_PATH_USES_DRIVE_LETTERS) | 
 |   // Windows can access case sensitive filesystems, so component | 
 |   // comparisions must be case sensitive, but drive letters are | 
 |   // never case sensitive. | 
 |   if ((FindDriveLetter(*parent_comp) != StringType::npos) && | 
 |       (FindDriveLetter(*child_comp) != StringType::npos)) { | 
 |     if (!StartsWith(*parent_comp, *child_comp, CompareCase::INSENSITIVE_ASCII)) | 
 |       return false; | 
 |     ++parent_comp; | 
 |     ++child_comp; | 
 |   } | 
 | #endif  // defined(FILE_PATH_USES_DRIVE_LETTERS) | 
 |  | 
 |   while (parent_comp != parent_components.end()) { | 
 |     if (*parent_comp != *child_comp) | 
 |       return false; | 
 |     ++parent_comp; | 
 |     ++child_comp; | 
 |   } | 
 |  | 
 |   if (path != nullptr) { | 
 |     for (; child_comp != child_components.end(); ++child_comp) { | 
 |       *path = path->Append(*child_comp); | 
 |     } | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | // libgen's dirname and basename aren't guaranteed to be thread-safe and aren't | 
 | // guaranteed to not modify their input strings, and in fact are implemented | 
 | // differently in this regard on different platforms.  Don't use them, but | 
 | // adhere to their behavior. | 
 | FilePath FilePath::DirName() const { | 
 |   FilePath new_path(path_); | 
 |   new_path.StripTrailingSeparatorsInternal(); | 
 |  | 
 |   // The drive letter, if any, always needs to remain in the output.  If there | 
 |   // is no drive letter, as will always be the case on platforms which do not | 
 |   // support drive letters, letter will be npos, or -1, so the comparisons and | 
 |   // resizes below using letter will still be valid. | 
 |   StringType::size_type letter = FindDriveLetter(new_path.path_); | 
 |  | 
 |   StringType::size_type last_separator = new_path.path_.find_last_of( | 
 |       kSeparators, StringType::npos, kSeparatorsLength - 1); | 
 |   if (last_separator == StringType::npos) { | 
 |     // path_ is in the current directory. | 
 |     new_path.path_.resize(letter + 1); | 
 |   } else if (last_separator == letter + 1) { | 
 |     // path_ is in the root directory. | 
 |     new_path.path_.resize(letter + 2); | 
 |   } else if (last_separator == letter + 2 && | 
 |              IsSeparator(new_path.path_[letter + 1])) { | 
 |     // path_ is in "//" (possibly with a drive letter); leave the double | 
 |     // separator intact indicating alternate root. | 
 |     new_path.path_.resize(letter + 3); | 
 |   } else if (last_separator != 0) { | 
 |     // path_ is somewhere else, trim the basename. | 
 |     new_path.path_.resize(last_separator); | 
 |   } | 
 |  | 
 |   new_path.StripTrailingSeparatorsInternal(); | 
 |   if (!new_path.path_.length()) | 
 |     new_path.path_ = kCurrentDirectory; | 
 |  | 
 |   return new_path; | 
 | } | 
 |  | 
 | FilePath FilePath::BaseName() const { | 
 |   FilePath new_path(path_); | 
 |   new_path.StripTrailingSeparatorsInternal(); | 
 |  | 
 |   // The drive letter, if any, is always stripped. | 
 |   StringType::size_type letter = FindDriveLetter(new_path.path_); | 
 |   if (letter != StringType::npos) { | 
 |     new_path.path_.erase(0, letter + 1); | 
 |   } | 
 |  | 
 |   // Keep everything after the final separator, but if the pathname is only | 
 |   // one character and it's a separator, leave it alone. | 
 |   StringType::size_type last_separator = new_path.path_.find_last_of( | 
 |       kSeparators, StringType::npos, kSeparatorsLength - 1); | 
 |   if (last_separator != StringType::npos && | 
 |       last_separator < new_path.path_.length() - 1) { | 
 |     new_path.path_.erase(0, last_separator + 1); | 
 |   } | 
 |  | 
 |   return new_path; | 
 | } | 
 |  | 
 | StringType FilePath::Extension() const { | 
 |   FilePath base(BaseName()); | 
 |   const StringType::size_type dot = ExtensionSeparatorPosition(base.path_); | 
 |   if (dot == StringType::npos) | 
 |     return StringType(); | 
 |  | 
 |   return base.path_.substr(dot, StringType::npos); | 
 | } | 
 |  | 
 | StringType FilePath::FinalExtension() const { | 
 |   FilePath base(BaseName()); | 
 |   const StringType::size_type dot = FinalExtensionSeparatorPosition(base.path_); | 
 |   if (dot == StringType::npos) | 
 |     return StringType(); | 
 |  | 
 |   return base.path_.substr(dot, StringType::npos); | 
 | } | 
 |  | 
 | FilePath FilePath::RemoveExtension() const { | 
 |   if (Extension().empty()) | 
 |     return *this; | 
 |  | 
 |   const StringType::size_type dot = ExtensionSeparatorPosition(path_); | 
 |   if (dot == StringType::npos) | 
 |     return *this; | 
 |  | 
 |   return FilePath(path_.substr(0, dot)); | 
 | } | 
 |  | 
 | FilePath FilePath::RemoveFinalExtension() const { | 
 |   if (FinalExtension().empty()) | 
 |     return *this; | 
 |  | 
 |   const StringType::size_type dot = FinalExtensionSeparatorPosition(path_); | 
 |   if (dot == StringType::npos) | 
 |     return *this; | 
 |  | 
 |   return FilePath(path_.substr(0, dot)); | 
 | } | 
 |  | 
 | FilePath FilePath::InsertBeforeExtension(StringViewType suffix) const { | 
 |   if (suffix.empty()) | 
 |     return FilePath(path_); | 
 |  | 
 |   if (IsEmptyOrSpecialCase(BaseName().value())) | 
 |     return FilePath(); | 
 |  | 
 |   StringType ext = Extension(); | 
 |   StringType ret = RemoveExtension().value(); | 
 |   ret.append(suffix); | 
 |   ret.append(ext); | 
 |   return FilePath(ret); | 
 | } | 
 |  | 
 | FilePath FilePath::InsertBeforeExtensionASCII(std::string_view suffix) const { | 
 |   DCHECK(IsStringASCII(suffix)); | 
 | #if defined(OS_WIN) | 
 |   return InsertBeforeExtension(ASCIIToUTF16(suffix)); | 
 | #elif defined(OS_POSIX) || defined(OS_FUCHSIA) | 
 |   return InsertBeforeExtension(suffix); | 
 | #endif | 
 | } | 
 |  | 
 | FilePath FilePath::AddExtension(StringViewType extension) const { | 
 |   if (IsEmptyOrSpecialCase(BaseName().value())) | 
 |     return FilePath(); | 
 |  | 
 |   // If the new extension is "" or ".", then just return the current FilePath. | 
 |   if (extension.empty() || | 
 |       (extension.size() == 1 && extension[0] == kExtensionSeparator)) | 
 |     return *this; | 
 |  | 
 |   StringType str = path_; | 
 |   if (extension[0] != kExtensionSeparator && | 
 |       *(str.end() - 1) != kExtensionSeparator) { | 
 |     str.append(1, kExtensionSeparator); | 
 |   } | 
 |   str.append(extension); | 
 |   return FilePath(str); | 
 | } | 
 |  | 
 | FilePath FilePath::ReplaceExtension(StringViewType extension) const { | 
 |   if (IsEmptyOrSpecialCase(BaseName().value())) | 
 |     return FilePath(); | 
 |  | 
 |   FilePath no_ext = RemoveExtension(); | 
 |   // If the new extension is "" or ".", then just remove the current extension. | 
 |   if (extension.empty() || | 
 |       (extension.size() == 1 && extension[0] == kExtensionSeparator)) | 
 |     return no_ext; | 
 |  | 
 |   StringType str = no_ext.value(); | 
 |   if (extension[0] != kExtensionSeparator) | 
 |     str.append(1, kExtensionSeparator); | 
 |   str.append(extension); | 
 |   return FilePath(str); | 
 | } | 
 |  | 
 | FilePath FilePath::Append(StringViewType component) const { | 
 |   StringViewType appended = component; | 
 |   StringType without_nuls; | 
 |  | 
 |   StringType::size_type nul_pos = component.find(kStringTerminator); | 
 |   if (nul_pos != StringViewType::npos) { | 
 |     without_nuls.assign(component.substr(0, nul_pos)); | 
 |     appended = StringViewType(without_nuls); | 
 |   } | 
 |  | 
 |   DCHECK(!IsPathAbsolute(appended)); | 
 |  | 
 |   if (path_.compare(kCurrentDirectory) == 0 && !appended.empty()) { | 
 |     // Append normally doesn't do any normalization, but as a special case, | 
 |     // when appending to kCurrentDirectory, just return a new path for the | 
 |     // component argument.  Appending component to kCurrentDirectory would | 
 |     // serve no purpose other than needlessly lengthening the path, and | 
 |     // it's likely in practice to wind up with FilePath objects containing | 
 |     // only kCurrentDirectory when calling DirName on a single relative path | 
 |     // component. | 
 |     return FilePath(appended); | 
 |   } | 
 |  | 
 |   FilePath new_path(path_); | 
 |   new_path.StripTrailingSeparatorsInternal(); | 
 |  | 
 |   // Don't append a separator if the path is empty (indicating the current | 
 |   // directory) or if the path component is empty (indicating nothing to | 
 |   // append). | 
 |   if (!appended.empty() && !new_path.path_.empty()) { | 
 |     // Don't append a separator if the path still ends with a trailing | 
 |     // separator after stripping (indicating the root directory). | 
 |     if (!IsSeparator(new_path.path_.back())) { | 
 |       // Don't append a separator if the path is just a drive letter. | 
 |       if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) { | 
 |         new_path.path_.append(1, kSeparators[0]); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   new_path.path_.append(appended); | 
 |   return new_path; | 
 | } | 
 |  | 
 | FilePath FilePath::Append(const FilePath& component) const { | 
 |   return Append(component.value()); | 
 | } | 
 |  | 
 | FilePath FilePath::AppendASCII(std::string_view component) const { | 
 |   DCHECK(base::IsStringASCII(component)); | 
 | #if defined(OS_WIN) | 
 |   return Append(ASCIIToUTF16(component)); | 
 | #elif defined(OS_POSIX) || defined(OS_FUCHSIA) | 
 |   return Append(component); | 
 | #endif | 
 | } | 
 |  | 
 | bool FilePath::IsAbsolute() const { | 
 |   return IsPathAbsolute(path_); | 
 | } | 
 |  | 
 | bool FilePath::EndsWithSeparator() const { | 
 |   if (empty()) | 
 |     return false; | 
 |   return IsSeparator(path_.back()); | 
 | } | 
 |  | 
 | FilePath FilePath::AsEndingWithSeparator() const { | 
 |   if (EndsWithSeparator() || path_.empty()) | 
 |     return *this; | 
 |  | 
 |   StringType path_str; | 
 |   path_str.reserve(path_.length() + 1);  // Only allocate string once. | 
 |  | 
 |   path_str = path_; | 
 |   path_str.append(&kSeparators[0], 1); | 
 |   return FilePath(path_str); | 
 | } | 
 |  | 
 | FilePath FilePath::StripTrailingSeparators() const { | 
 |   FilePath new_path(path_); | 
 |   new_path.StripTrailingSeparatorsInternal(); | 
 |  | 
 |   return new_path; | 
 | } | 
 |  | 
 | bool FilePath::ReferencesParent() const { | 
 |   if (path_.find(kParentDirectory) == StringType::npos) { | 
 |     // GetComponents is quite expensive, so avoid calling it in the majority | 
 |     // of cases where there isn't a kParentDirectory anywhere in the path. | 
 |     return false; | 
 |   } | 
 |  | 
 |   std::vector<StringType> components; | 
 |   GetComponents(&components); | 
 |  | 
 |   std::vector<StringType>::const_iterator it = components.begin(); | 
 |   for (; it != components.end(); ++it) { | 
 |     const StringType& component = *it; | 
 |     // Windows has odd, undocumented behavior with path components containing | 
 |     // only whitespace and . characters. So, if all we see is . and | 
 |     // whitespace, then we treat any .. sequence as referencing parent. | 
 |     // For simplicity we enforce this on all platforms. | 
 |     if (component.find_first_not_of(FILE_PATH_LITERAL(". \n\r\t")) == | 
 |             std::string::npos && | 
 |         component.find(kParentDirectory) != std::string::npos) { | 
 |       return true; | 
 |     } | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | #if defined(OS_WIN) | 
 |  | 
 | std::u16string FilePath::LossyDisplayName() const { | 
 |   return path_; | 
 | } | 
 |  | 
 | std::string FilePath::MaybeAsASCII() const { | 
 |   if (base::IsStringASCII(path_)) | 
 |     return UTF16ToASCII(path_); | 
 |   return std::string(); | 
 | } | 
 |  | 
 | std::string FilePath::As8Bit() const { | 
 |   return UTF16ToUTF8(value()); | 
 | } | 
 |  | 
 | #elif defined(OS_POSIX) || defined(OS_FUCHSIA) | 
 |  | 
 | // See file_path.h for a discussion of the encoding of paths on POSIX | 
 | // platforms.  These encoding conversion functions are not quite correct. | 
 |  | 
 | std::string FilePath::MaybeAsASCII() const { | 
 |   if (base::IsStringASCII(path_)) | 
 |     return path_; | 
 |   return std::string(); | 
 | } | 
 |  | 
 | std::string FilePath::As8Bit() const { | 
 |   return value(); | 
 | } | 
 |  | 
 | #endif  // defined(OS_WIN) | 
 |  | 
 | void FilePath::StripTrailingSeparatorsInternal() { | 
 |   // If there is no drive letter, start will be 1, which will prevent stripping | 
 |   // the leading separator if there is only one separator.  If there is a drive | 
 |   // letter, start will be set appropriately to prevent stripping the first | 
 |   // separator following the drive letter, if a separator immediately follows | 
 |   // the drive letter. | 
 |   StringType::size_type start = FindDriveLetter(path_) + 2; | 
 |  | 
 |   StringType::size_type last_stripped = StringType::npos; | 
 |   for (StringType::size_type pos = path_.length(); | 
 |        pos > start && IsSeparator(path_[pos - 1]); --pos) { | 
 |     // If the string only has two separators and they're at the beginning, | 
 |     // don't strip them, unless the string began with more than two separators. | 
 |     if (pos != start + 1 || last_stripped == start + 2 || | 
 |         !IsSeparator(path_[start - 1])) { | 
 |       path_.resize(pos - 1); | 
 |       last_stripped = pos; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | FilePath FilePath::NormalizePathSeparators() const { | 
 |   return NormalizePathSeparatorsTo(kSeparators[0]); | 
 | } | 
 |  | 
 | FilePath FilePath::NormalizePathSeparatorsTo(CharType separator) const { | 
 | #if defined(FILE_PATH_USES_WIN_SEPARATORS) | 
 |   DCHECK_NE(kSeparators + kSeparatorsLength, | 
 |             std::find(kSeparators, kSeparators + kSeparatorsLength, separator)); | 
 |   StringType copy = path_; | 
 |   for (size_t i = 0; i < kSeparatorsLength; ++i) { | 
 |     std::replace(copy.begin(), copy.end(), kSeparators[i], separator); | 
 |   } | 
 |   return FilePath(copy); | 
 | #else | 
 |   return *this; | 
 | #endif | 
 | } | 
 |  | 
 | }  // namespace base |