| // Copyright (c) 2013 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 "tools/gn/path_output.h" | 
 |  | 
 | #include "base/strings/string_util.h" | 
 | #include "tools/gn/filesystem_utils.h" | 
 | #include "tools/gn/output_file.h" | 
 | #include "tools/gn/string_utils.h" | 
 | #include "util/build_config.h" | 
 |  | 
 | PathOutput::PathOutput(const SourceDir& current_dir, | 
 |                        const std::string_view& source_root, | 
 |                        EscapingMode escaping) | 
 |     : current_dir_(current_dir) { | 
 |   inverse_current_dir_ = RebasePath("//", current_dir, source_root); | 
 |   if (!EndsWithSlash(inverse_current_dir_)) | 
 |     inverse_current_dir_.push_back('/'); | 
 |   options_.mode = escaping; | 
 | } | 
 |  | 
 | PathOutput::~PathOutput() = default; | 
 |  | 
 | void PathOutput::WriteFile(std::ostream& out, const SourceFile& file) const { | 
 |   WritePathStr(out, file.value()); | 
 | } | 
 |  | 
 | void PathOutput::WriteDir(std::ostream& out, | 
 |                           const SourceDir& dir, | 
 |                           DirSlashEnding slash_ending) const { | 
 |   if (dir.value() == "/") { | 
 |     // Writing system root is always a slash (this will normally only come up | 
 |     // on Posix systems). | 
 |     if (slash_ending == DIR_NO_LAST_SLASH) | 
 |       out << "/."; | 
 |     else | 
 |       out << "/"; | 
 |   } else if (dir.value() == "//") { | 
 |     // Writing out the source root. | 
 |     if (slash_ending == DIR_NO_LAST_SLASH) { | 
 |       // The inverse_current_dir_ will contain a [back]slash at the end, so we | 
 |       // can't just write it out. | 
 |       if (inverse_current_dir_.empty()) { | 
 |         out << "."; | 
 |       } else { | 
 |         out.write(inverse_current_dir_.c_str(), | 
 |                   inverse_current_dir_.size() - 1); | 
 |       } | 
 |     } else { | 
 |       if (inverse_current_dir_.empty()) | 
 |         out << "./"; | 
 |       else | 
 |         out << inverse_current_dir_; | 
 |     } | 
 |   } else if (dir == current_dir_) { | 
 |     // Writing the same directory. This needs special handling here since | 
 |     // we need to output something else other than the input. | 
 |     if (slash_ending == DIR_INCLUDE_LAST_SLASH) | 
 |       out << "./"; | 
 |     else | 
 |       out << "."; | 
 |   } else if (slash_ending == DIR_INCLUDE_LAST_SLASH) { | 
 |     WritePathStr(out, dir.value()); | 
 |   } else { | 
 |     // DIR_NO_LAST_SLASH mode, just trim the last char. | 
 |     WritePathStr(out, | 
 |                  std::string_view(dir.value().data(), dir.value().size() - 1)); | 
 |   } | 
 | } | 
 |  | 
 | void PathOutput::WriteFile(std::ostream& out, const OutputFile& file) const { | 
 |   // Here we assume that the path is already preprocessed. | 
 |   EscapeStringToStream(out, file.value(), options_); | 
 | } | 
 |  | 
 | void PathOutput::WriteFiles(std::ostream& out, | 
 |                             const std::vector<OutputFile>& files) const { | 
 |   for (const auto& file : files) { | 
 |     out << " "; | 
 |     WriteFile(out, file); | 
 |   } | 
 | } | 
 |  | 
 | void PathOutput::WriteFiles(std::ostream& out, | 
 |                             const UniqueVector<OutputFile>& files) const { | 
 |   for (const auto& file : files) { | 
 |     out << " "; | 
 |     WriteFile(out, file); | 
 |   } | 
 | } | 
 |  | 
 | void PathOutput::WriteDir(std::ostream& out, | 
 |                           const OutputFile& file, | 
 |                           DirSlashEnding slash_ending) const { | 
 |   DCHECK(file.value().empty() || file.value()[file.value().size() - 1] == '/'); | 
 |  | 
 |   switch (slash_ending) { | 
 |     case DIR_INCLUDE_LAST_SLASH: | 
 |       EscapeStringToStream(out, file.value(), options_); | 
 |       break; | 
 |     case DIR_NO_LAST_SLASH: | 
 |       if (!file.value().empty() && | 
 |           file.value()[file.value().size() - 1] == '/') { | 
 |         // Trim trailing slash. | 
 |         EscapeStringToStream( | 
 |             out, std::string_view(file.value().data(), file.value().size() - 1), | 
 |             options_); | 
 |       } else { | 
 |         // Doesn't end with a slash, write the whole thing. | 
 |         EscapeStringToStream(out, file.value(), options_); | 
 |       } | 
 |       break; | 
 |   } | 
 | } | 
 |  | 
 | void PathOutput::WriteFile(std::ostream& out, | 
 |                            const base::FilePath& file) const { | 
 |   // Assume native file paths are always absolute. | 
 |   EscapeStringToStream(out, FilePathToUTF8(file), options_); | 
 | } | 
 |  | 
 | void PathOutput::WriteSourceRelativeString(std::ostream& out, | 
 |                                            const std::string_view& str) const { | 
 |   if (options_.mode == ESCAPE_NINJA_COMMAND) { | 
 |     // Shell escaping needs an intermediate string since it may end up | 
 |     // quoting the whole thing. | 
 |     std::string intermediate; | 
 |     intermediate.reserve(inverse_current_dir_.size() + str.size()); | 
 |     intermediate.assign(inverse_current_dir_.c_str(), | 
 |                         inverse_current_dir_.size()); | 
 |     intermediate.append(str.data(), str.size()); | 
 |  | 
 |     EscapeStringToStream( | 
 |         out, std::string_view(intermediate.c_str(), intermediate.size()), | 
 |         options_); | 
 |   } else { | 
 |     // Ninja (and none) escaping can avoid the intermediate string and | 
 |     // reprocessing of the inverse_current_dir_. | 
 |     out << inverse_current_dir_; | 
 |     EscapeStringToStream(out, str, options_); | 
 |   } | 
 | } | 
 |  | 
 | void PathOutput::WritePathStr(std::ostream& out, | 
 |                               const std::string_view& str) const { | 
 |   DCHECK(str.size() > 0 && str[0] == '/'); | 
 |  | 
 |   if (str.substr(0, current_dir_.value().size()) == | 
 |       std::string_view(current_dir_.value())) { | 
 |     // The current dir is a prefix of the output file, so we can strip the | 
 |     // prefix and write out the result. | 
 |     EscapeStringToStream(out, str.substr(current_dir_.value().size()), | 
 |                          options_); | 
 |   } else if (str.size() >= 2 && str[1] == '/') { | 
 |     WriteSourceRelativeString(out, str.substr(2)); | 
 |   } else { | 
 | // Input begins with one slash, don't write the current directory since | 
 | // it's system-absolute. | 
 | #if defined(OS_WIN) | 
 |     // On Windows, trim the leading slash, since the input for absolute | 
 |     // paths will look like "/C:/foo/bar.txt". | 
 |     EscapeStringToStream(out, str.substr(1), options_); | 
 | #else | 
 |     EscapeStringToStream(out, str, options_); | 
 | #endif | 
 |   } | 
 | } |