|  | /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying | 
|  | file LICENSE.rst or https://cmake.org/licensing for details.  */ | 
|  | #include "cmGccDepfileLexerHelper.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <cstdio> | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | #include "cmGccDepfileReaderTypes.h" | 
|  |  | 
|  | #include "LexerParser/cmGccDepfileLexer.h" | 
|  |  | 
|  | #ifdef _WIN32 | 
|  | #  include <cctype> | 
|  |  | 
|  | #  include "cmsys/Encoding.h" | 
|  | #endif | 
|  |  | 
|  | bool cmGccDepfileLexerHelper::readFile(char const* filePath) | 
|  | { | 
|  | #ifdef _WIN32 | 
|  | wchar_t* wpath = cmsysEncoding_DupToWide(filePath); | 
|  | FILE* file = _wfopen(wpath, L"rb"); | 
|  | free(wpath); | 
|  | #else | 
|  | FILE* file = fopen(filePath, "r"); | 
|  | #endif | 
|  | if (!file) { | 
|  | return false; | 
|  | } | 
|  | this->newEntry(); | 
|  | yyscan_t scanner; | 
|  | cmGccDepfile_yylex_init(&scanner); | 
|  | cmGccDepfile_yyset_extra(this, scanner); | 
|  | cmGccDepfile_yyrestart(file, scanner); | 
|  | cmGccDepfile_yylex(scanner); | 
|  | cmGccDepfile_yylex_destroy(scanner); | 
|  | this->sanitizeContent(); | 
|  | fclose(file); | 
|  | return this->HelperState != State::Failed; | 
|  | } | 
|  |  | 
|  | void cmGccDepfileLexerHelper::newEntry() | 
|  | { | 
|  | if (this->HelperState == State::Rule && !this->Content.empty()) { | 
|  | if (!this->Content.back().rules.empty() && | 
|  | !this->Content.back().rules.back().empty()) { | 
|  | this->HelperState = State::Failed; | 
|  | } | 
|  | return; | 
|  | } | 
|  | this->HelperState = State::Rule; | 
|  | this->Content.emplace_back(); | 
|  | this->newRule(); | 
|  | } | 
|  |  | 
|  | void cmGccDepfileLexerHelper::newRule() | 
|  | { | 
|  | auto& entry = this->Content.back(); | 
|  | if (entry.rules.empty() || !entry.rules.back().empty()) { | 
|  | entry.rules.emplace_back(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void cmGccDepfileLexerHelper::newDependency() | 
|  | { | 
|  | if (this->HelperState == State::Failed) { | 
|  | return; | 
|  | } | 
|  | this->HelperState = State::Dependency; | 
|  | auto& entry = this->Content.back(); | 
|  | if (entry.paths.empty() || !entry.paths.back().empty()) { | 
|  | entry.paths.emplace_back(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void cmGccDepfileLexerHelper::newRuleOrDependency() | 
|  | { | 
|  | if (this->HelperState == State::Rule) { | 
|  | this->newRule(); | 
|  | } else if (this->HelperState == State::Dependency) { | 
|  | this->newDependency(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void cmGccDepfileLexerHelper::addToCurrentPath(char const* s) | 
|  | { | 
|  | if (this->Content.empty()) { | 
|  | return; | 
|  | } | 
|  | cmGccStyleDependency* dep = &this->Content.back(); | 
|  | std::string* dst = nullptr; | 
|  | switch (this->HelperState) { | 
|  | case State::Rule: { | 
|  | if (dep->rules.empty()) { | 
|  | return; | 
|  | } | 
|  | dst = &dep->rules.back(); | 
|  | } break; | 
|  | case State::Dependency: { | 
|  | if (dep->paths.empty()) { | 
|  | return; | 
|  | } | 
|  | dst = &dep->paths.back(); | 
|  | } break; | 
|  | case State::Failed: | 
|  | return; | 
|  | } | 
|  | dst->append(s); | 
|  | } | 
|  |  | 
|  | void cmGccDepfileLexerHelper::sanitizeContent() | 
|  | { | 
|  | for (auto it = this->Content.begin(); it != this->Content.end();) { | 
|  | // remove duplicate path entries | 
|  | std::sort(it->paths.begin(), it->paths.end()); | 
|  | auto last = std::unique(it->paths.begin(), it->paths.end()); | 
|  | it->paths.erase(last, it->paths.end()); | 
|  |  | 
|  | // Remove empty paths and normalize windows paths | 
|  | for (auto pit = it->paths.begin(); pit != it->paths.end();) { | 
|  | if (pit->empty()) { | 
|  | pit = it->paths.erase(pit); | 
|  | } else { | 
|  | #if defined(_WIN32) | 
|  | // Unescape the colon following the drive letter. | 
|  | // Some versions of GNU compilers can escape this character. | 
|  | // c\:\path must be transformed to c:\path | 
|  | if (pit->size() >= 3 && std::toupper((*pit)[0]) >= 'A' && | 
|  | std::toupper((*pit)[0]) <= 'Z' && (*pit)[1] == '\\' && | 
|  | (*pit)[2] == ':') { | 
|  | pit->erase(1, 1); | 
|  | } | 
|  | #endif | 
|  | ++pit; | 
|  | } | 
|  | } | 
|  | // Remove empty rules | 
|  | for (auto rit = it->rules.begin(); rit != it->rules.end();) { | 
|  | if (rit->empty()) { | 
|  | rit = it->rules.erase(rit); | 
|  | } else { | 
|  | ++rit; | 
|  | } | 
|  | } | 
|  | // Remove the entry if rules are empty | 
|  | if (it->rules.empty()) { | 
|  | it = this->Content.erase(it); | 
|  | } else { | 
|  | ++it; | 
|  | } | 
|  | } | 
|  | } |