Chromium Code Reviews| Index: tools/gn/c_include_iterator.cc |
| diff --git a/tools/gn/c_include_iterator.cc b/tools/gn/c_include_iterator.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..214fbc2564b64bc77419177051b220ab8790291d |
| --- /dev/null |
| +++ b/tools/gn/c_include_iterator.cc |
| @@ -0,0 +1,142 @@ |
| +// Copyright 2014 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/c_include_iterator.h" |
| + |
| +#include "base/logging.h" |
| + |
| +namespace { |
| + |
| +const int kMaxNonIncludeLines = 10; |
| + |
| +enum IncludeType { |
| + INCLUDE_NONE, |
| + INCLUDE_SYSTEM, // #include <...> |
| + INCLUDE_USER // #include "..." |
| +}; |
| + |
| +// Returns true if str starts with the prefix. |
| +bool StartsWith(const base::StringPiece& str, const base::StringPiece& prefix) { |
| + base::StringPiece extracted = str.substr(0, prefix.size()); |
| + return extracted == prefix; |
| +} |
| + |
| +// Returns a new string piece referencing the same buffer as the argument, but |
| +// with leading space trimmed. This only checks for space and tab characters |
| +// since we're dealing with lines in C source files. |
| +base::StringPiece TrimLeadingWhitespace(const base::StringPiece& str) { |
| + size_t new_begin = 0; |
| + while (new_begin < str.size() && |
| + (str[new_begin] == ' ' || str[new_begin] == '\t')) |
| + new_begin++; |
| + return str.substr(new_begin); |
| +} |
| + |
| +// We don't want to count comment lines and preprocessor lines toward our |
| +// "max lines to look at before giving up" since the beginnings of some files |
| +// may have a lot of comments. |
| +// |
| +// We on't handle C-style "//" comments since this is the normal commenting |
|
scottmg
2014/04/05 00:40:07
"only"
|
| +// style used in Chrome, and do so pretty stupidly. We don't want to write a |
| +// full C++ parser here, we're just trying to get a good heuristic for checking |
| +// the file. |
| +// |
| +// We assume the line has leading whitespace trimmed. We also assume that empty |
| +// lines have already been filtered out. |
| +bool ShouldCountTowardNonIncludeLines(const base::StringPiece& line) { |
| + if (StartsWith(line, "//")) |
| + return false; // Don't count comments. |
| + if (StartsWith(line, "#")) |
| + return false; // Don't count preprocessor. |
| + return true; // Count everything else. |
| +} |
| + |
| +// Given a line, checks to see if it looks like an include or import and |
| +// extract the path. The type of include is returned. Returns INCLUDE_NONE on |
| +// error or if this is not an include line. |
| +IncludeType ExtractInclude(const base::StringPiece& line, |
| + base::StringPiece* path) { |
| + static const char kInclude[] = "#include"; |
| + static const size_t kIncludeLen = arraysize(kInclude) - 1; // No null. |
| + static const char kImport[] = "#import"; |
| + static const size_t kImportLen = arraysize(kImport) - 1; // No null. |
| + |
| + base::StringPiece contents; |
| + if (StartsWith(line, base::StringPiece(kInclude, kIncludeLen))) |
| + contents = TrimLeadingWhitespace(line.substr(kIncludeLen)); |
| + else if (StartsWith(line, base::StringPiece(kImport, kImportLen))) |
| + contents = TrimLeadingWhitespace(line.substr(kImportLen)); |
| + |
| + if (contents.empty()) |
| + return INCLUDE_NONE; |
| + |
| + IncludeType type = INCLUDE_NONE; |
| + char terminating_char = 0; |
| + if (contents[0] == '"') { |
| + type = INCLUDE_USER; |
| + terminating_char = '"'; |
| + } else if (contents[0] == '<') { |
| + type = INCLUDE_SYSTEM; |
| + terminating_char = '>'; |
| + } else { |
| + return INCLUDE_NONE; |
| + } |
| + |
| + // Count everything to next "/> as the contents. |
| + size_t terminator_index = contents.find(terminating_char, 1); |
| + if (terminator_index == base::StringPiece::npos) |
| + return INCLUDE_NONE; |
| + |
| + *path = contents.substr(1, terminator_index - 1); |
| + return type; |
| +} |
| + |
| +} // namespace |
| + |
| +CIncludeIterator::CIncludeIterator(const base::StringPiece& file) |
| + : file_(file), |
| + offset_(0), |
| + lines_since_last_include_(0) { |
| +} |
| + |
| +CIncludeIterator::~CIncludeIterator() { |
| +} |
| + |
| +bool CIncludeIterator::GetNextIncludeString(base::StringPiece* out) { |
| + base::StringPiece line; |
| + while (lines_since_last_include_ < kMaxNonIncludeLines && |
| + GetNextLine(&line)) { |
| + base::StringPiece trimmed = TrimLeadingWhitespace(line); |
| + if (trimmed.empty()) |
| + continue; // Just ignore all empty lines. |
| + |
| + base::StringPiece include_contents; |
| + IncludeType type = ExtractInclude(trimmed, &include_contents); |
| + if (type == INCLUDE_USER) { |
| + // Only count user includes for now. |
| + *out = include_contents; |
| + return true; |
| + } |
| + |
| + if (ShouldCountTowardNonIncludeLines(trimmed)) |
| + lines_since_last_include_++; |
|
scottmg
2014/04/05 00:40:07
needs to get reset, or renamed. and a test for thi
|
| + } |
| + return false; |
| +} |
| + |
| +bool CIncludeIterator::GetNextLine(base::StringPiece* line) { |
| + if (offset_ == file_.size()) |
| + return false; |
| + |
| + size_t begin = offset_; |
| + while (offset_ < file_.size() && file_[offset_] != '\n') |
| + offset_++; |
| + |
| + *line = file_.substr(begin, offset_ - begin); |
| + |
| + // If we didn't hit EOF, skip past the newline for the next one. |
| + if (offset_ < file_.size()) |
| + offset_++; |
| + return true; |
| +} |