| Index: tools/gn/filesystem_utils.cc | 
| diff --git a/tools/gn/filesystem_utils.cc b/tools/gn/filesystem_utils.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..625a358a791e6247b7f4f516edce83dddfb08ff5 | 
| --- /dev/null | 
| +++ b/tools/gn/filesystem_utils.cc | 
| @@ -0,0 +1,350 @@ | 
| +// 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/filesystem_utils.h" | 
| + | 
| +#include "base/logging.h" | 
| +#include "base/strings/utf_string_conversions.h" | 
| +#include "build/build_config.h" | 
| +#include "tools/gn/location.h" | 
| +#include "tools/gn/source_dir.h" | 
| + | 
| +namespace { | 
| + | 
| +enum DotDisposition { | 
| +  // The given dot is just part of a filename and is not special. | 
| +  NOT_A_DIRECTORY, | 
| + | 
| +  // The given dot is the current directory. | 
| +  DIRECTORY_CUR, | 
| + | 
| +  // The given dot is the first of a double dot that should take us up one. | 
| +  DIRECTORY_UP | 
| +}; | 
| + | 
| +// When we find a dot, this function is called with the character following | 
| +// that dot to see what it is. The return value indicates what type this dot is | 
| +// (see above). This code handles the case where the dot is at the end of the | 
| +// input. | 
| +// | 
| +// |*consumed_len| will contain the number of characters in the input that | 
| +// express what we found. | 
| +DotDisposition ClassifyAfterDot(const std::string& path, | 
| +                                size_t after_dot, | 
| +                                size_t* consumed_len) { | 
| +  if (after_dot == path.size()) { | 
| +    // Single dot at the end. | 
| +    *consumed_len = 1; | 
| +    return DIRECTORY_CUR; | 
| +  } | 
| +  if (path[after_dot] == '/') { | 
| +    // Single dot followed by a slash. | 
| +    *consumed_len = 2;  // Consume the slash | 
| +    return DIRECTORY_CUR; | 
| +  } | 
| + | 
| +  if (path[after_dot] == '.') { | 
| +    // Two dots. | 
| +    if (after_dot + 1 == path.size()) { | 
| +      // Double dot at the end. | 
| +      *consumed_len = 2; | 
| +      return DIRECTORY_UP; | 
| +    } | 
| +    if (path[after_dot + 1] == '/') { | 
| +      // Double dot folowed by a slash. | 
| +      *consumed_len = 3; | 
| +      return DIRECTORY_UP; | 
| +    } | 
| +  } | 
| + | 
| +  // The dots are followed by something else, not a directory. | 
| +  *consumed_len = 1; | 
| +  return NOT_A_DIRECTORY; | 
| +} | 
| + | 
| +}  // namesapce | 
| + | 
| +SourceFileType GetSourceFileType(const SourceFile& file, | 
| +                                 Settings::TargetOS os) { | 
| +  base::StringPiece extension = FindExtension(&file.value()); | 
| +  if (extension == "cc" || extension == "cpp" || extension == "cxx") | 
| +    return SOURCE_CC; | 
| +  if (extension == "h") | 
| +    return SOURCE_H; | 
| +  if (extension == "c") | 
| +    return SOURCE_C; | 
| + | 
| +  switch (os) { | 
| +    case Settings::MAC: | 
| +      if (extension == "m") | 
| +        return SOURCE_M; | 
| +      if (extension == "mm") | 
| +        return SOURCE_MM; | 
| +      break; | 
| + | 
| +    case Settings::WIN: | 
| +      if (extension == "rc") | 
| +        return SOURCE_RC; | 
| +      break; | 
| + | 
| +    default: | 
| +      break; | 
| +  } | 
| + | 
| +  // TODO(brettw) asm files. | 
| +  // TODO(brettw) weird thing with .S on non-Windows platforms. | 
| +  return SOURCE_UNKNOWN; | 
| +} | 
| + | 
| +const char* GetExtensionForOutputType(Target::OutputType type, | 
| +                                      Settings::TargetOS os) { | 
| +  switch (os) { | 
| +    case Settings::WIN: | 
| +      switch (type) { | 
| +        case Target::NONE: | 
| +          NOTREACHED(); | 
| +          return ""; | 
| +        case Target::EXECUTABLE: | 
| +          return "exe"; | 
| +        case Target::SHARED_LIBRARY: | 
| +          return "dll.lib";  // Extension of import library. | 
| +        case Target::STATIC_LIBRARY: | 
| +          return "lib"; | 
| +        case Target::LOADABLE_MODULE: | 
| +          return "dll";  // TODO(brettw) what's this? | 
| +        default: | 
| +          NOTREACHED(); | 
| +      } | 
| +      break; | 
| + | 
| +    default: | 
| +      NOTREACHED(); | 
| +  } | 
| +  return ""; | 
| +} | 
| + | 
| +std::string FilePathToUTF8(const base::FilePath& path) { | 
| +#if defined(OS_WIN) | 
| +  return WideToUTF8(path.value()); | 
| +#else | 
| +  return path.value(); | 
| +#endif | 
| +} | 
| + | 
| +base::FilePath UTF8ToFilePath(const base::StringPiece& sp) { | 
| +#if defined(OS_WIN) | 
| +  return base::FilePath(UTF8ToWide(sp)); | 
| +#else | 
| +  return base::FilePath(sp.as_string()); | 
| +#endif | 
| +} | 
| + | 
| +size_t FindExtensionOffset(const std::string& path) { | 
| +  for (int i = static_cast<int>(path.size()); i >= 0; i--) { | 
| +    if (path[i] == '/') | 
| +      break; | 
| +    if (path[i] == '.') | 
| +      return i + 1; | 
| +  } | 
| +  return std::string::npos; | 
| +} | 
| + | 
| +base::StringPiece FindExtension(const std::string* path) { | 
| +  size_t extension_offset = FindExtensionOffset(*path); | 
| +  if (extension_offset == std::string::npos) | 
| +    return base::StringPiece(); | 
| +  return base::StringPiece(&path->data()[extension_offset], | 
| +                           path->size() - extension_offset); | 
| +} | 
| + | 
| +size_t FindFilenameOffset(const std::string& path) { | 
| +  for (int i = static_cast<int>(path.size()) - 1; i >= 0; i--) { | 
| +    if (path[i] == '/') | 
| +      return i + 1; | 
| +  } | 
| +  return 0;  // No filename found means everything was the filename. | 
| +} | 
| + | 
| +base::StringPiece FindFilename(const std::string* path) { | 
| +  size_t filename_offset = FindFilenameOffset(*path); | 
| +  if (filename_offset == 0) | 
| +    return base::StringPiece(*path);  // Everything is the file name. | 
| +  return base::StringPiece(&(*path).data()[filename_offset], | 
| +                           path->size() - filename_offset); | 
| +} | 
| + | 
| +base::StringPiece FindFilenameNoExtension(const std::string* path) { | 
| +  if (path->empty()) | 
| +    return base::StringPiece(); | 
| +  size_t filename_offset = FindFilenameOffset(*path); | 
| +  size_t extension_offset = FindExtensionOffset(*path); | 
| + | 
| +  size_t name_len; | 
| +  if (extension_offset == std::string::npos) | 
| +    name_len = path->size() - filename_offset; | 
| +  else | 
| +    name_len = extension_offset - filename_offset - 1; | 
| + | 
| +  return base::StringPiece(&(*path).data()[filename_offset], name_len); | 
| +} | 
| + | 
| +void RemoveFilename(std::string* path) { | 
| +  path->resize(FindFilenameOffset(*path)); | 
| +} | 
| + | 
| +bool EndsWithSlash(const std::string& s) { | 
| +  return !s.empty() && s[s.size() - 1] == '/'; | 
| +} | 
| + | 
| +base::StringPiece FindDir(const std::string* path) { | 
| +  size_t filename_offset = FindFilenameOffset(*path); | 
| +  if (filename_offset == 0u) | 
| +    return base::StringPiece(); | 
| +  return base::StringPiece(path->data(), filename_offset); | 
| +} | 
| + | 
| +bool EnsureStringIsInOutputDir(const SourceDir& dir, | 
| +                               const std::string& str, | 
| +                               const Value& originating, | 
| +                               Err* err) { | 
| +  // The last char of the dir will be a slash. We don't care if the input ends | 
| +  // in a slash or not, so just compare up until there. | 
| +  // | 
| +  // This check will be wrong for all proper prefixes "e.g. "/output" will | 
| +  // match "/out" but we don't really care since this is just a sanity check. | 
| +  const std::string& dir_str = dir.value(); | 
| +  if (str.compare(0, dir_str.length() - 1, dir_str, 0, dir_str.length() - 1) | 
| +      != 0) { | 
| +    *err = Err(originating, "File not inside output directory.", | 
| +        "The given file should be in the output directory. Normally you would " | 
| +        "specify\n\"$target_output_dir/foo\" or " | 
| +        "\"$target_gen_dir/foo\". I interpreted this as\n\"" | 
| +        + str + "\"."); | 
| +    return false; | 
| +  } | 
| +  return true; | 
| +} | 
| + | 
| +std::string InvertDir(const SourceDir& path) { | 
| +  const std::string value = path.value(); | 
| +  if (value.empty()) | 
| +    return std::string(); | 
| + | 
| +  DCHECK(value[0] == '/'); | 
| +  size_t begin_index = 1; | 
| + | 
| +  // If the input begins with two slashes, skip over both (this is a | 
| +  // source-relative dir). | 
| +  if (value.size() > 1 && value[1] == '/') | 
| +    begin_index = 2; | 
| + | 
| +  std::string ret; | 
| +  for (size_t i = begin_index; i < value.size(); i++) { | 
| +    if (value[i] == '/') | 
| +      ret.append("../"); | 
| +  } | 
| +  return ret; | 
| +} | 
| + | 
| +void NormalizePath(std::string* path) { | 
| +  char* pathbuf = path->empty() ? NULL : &(*path)[0]; | 
| + | 
| +  // top_index is the first character we can modify in the path. Anything | 
| +  // before this indicates where the path is relative to. | 
| +  size_t top_index = 0; | 
| +  bool is_relative = true; | 
| +  if (!path->empty() && pathbuf[0] == '/') { | 
| +    is_relative = false; | 
| + | 
| +    if (path->size() > 1 && pathbuf[1] == '/') { | 
| +      // Two leading slashes, this is a path into the source dir. | 
| +      top_index = 2; | 
| +    } else { | 
| +      // One leading slash, this is a system-absolute path. | 
| +      top_index = 1; | 
| +    } | 
| +  } | 
| + | 
| +  size_t dest_i = top_index; | 
| +  for (size_t src_i = top_index; src_i < path->size(); /* nothing */) { | 
| +    if (pathbuf[src_i] == '.') { | 
| +      if (src_i == 0 || pathbuf[src_i - 1] == '/') { | 
| +        // Slash followed by a dot, see if it's something special. | 
| +        size_t consumed_len; | 
| +        switch (ClassifyAfterDot(*path, src_i + 1, &consumed_len)) { | 
| +          case NOT_A_DIRECTORY: | 
| +            // Copy the dot to the output, it means nothing special. | 
| +            pathbuf[dest_i++] = pathbuf[src_i++]; | 
| +            break; | 
| +          case DIRECTORY_CUR: | 
| +            // Current directory, just skip the input. | 
| +            src_i += consumed_len; | 
| +            break; | 
| +          case DIRECTORY_UP: | 
| +            // Back up over previous directory component. If we're already | 
| +            // at the top, preserve the "..". | 
| +            if (dest_i > top_index) { | 
| +              // The previous char was a slash, remove it. | 
| +              dest_i--; | 
| +            } | 
| + | 
| +            if (dest_i == top_index) { | 
| +              if (is_relative) { | 
| +                // We're already at the beginning of a relative input, copy the | 
| +                // ".." and continue. We need the trailing slash if there was | 
| +                // one before (otherwise we're at the end of the input). | 
| +                pathbuf[dest_i++] = '.'; | 
| +                pathbuf[dest_i++] = '.'; | 
| +                if (consumed_len == 3) | 
| +                  pathbuf[dest_i++] = '/'; | 
| + | 
| +                // This also makes a new "root" that we can't delete by going | 
| +                // up more levels.  Otherwise "../.." would collapse to | 
| +                // nothing. | 
| +                top_index = dest_i; | 
| +              } | 
| +              // Otherwise we're at the beginning of an absolute path. Don't | 
| +              // allow ".." to go up another level and just eat it. | 
| +            } else { | 
| +              // Just find the previous slash or the beginning of input. | 
| +              while (dest_i > 0 && pathbuf[dest_i - 1] != '/') | 
| +                dest_i--; | 
| +            } | 
| +            src_i += consumed_len; | 
| +        } | 
| +      } else { | 
| +        // Dot not preceeded by a slash, copy it literally. | 
| +        pathbuf[dest_i++] = pathbuf[src_i++]; | 
| +      } | 
| +    } else if (pathbuf[src_i] == '/') { | 
| +      if (src_i > 0 && pathbuf[src_i - 1] == '/') { | 
| +        // Two slashes in a row, skip over it. | 
| +        src_i++; | 
| +      } else { | 
| +        // Just one slash, copy it. | 
| +        pathbuf[dest_i++] = pathbuf[src_i++]; | 
| +      } | 
| +    } else { | 
| +      // Input nothing special, just copy it. | 
| +      pathbuf[dest_i++] = pathbuf[src_i++]; | 
| +    } | 
| +  } | 
| +  path->resize(dest_i); | 
| +} | 
| + | 
| +void ConvertPathToSystem(std::string* path) { | 
| +#if defined(OS_WIN) | 
| +  for (size_t i = 0; i < path->size(); i++) { | 
| +    if ((*path)[i] == '/') | 
| +      (*path)[i] = '\\'; | 
| +  } | 
| +#endif | 
| +} | 
| + | 
| +std::string PathToSystem(const std::string& path) { | 
| +  std::string ret(path); | 
| +  ConvertPathToSystem(&ret); | 
| +  return ret; | 
| +} | 
| + | 
|  |