cmake/Source/cmVisualStudioSlnParser.cxx

675 lines
20 KiB
C++
Raw Normal View History

2016-10-30 18:24:19 +01:00
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
2013-11-03 12:27:13 +02:00
#include "cmVisualStudioSlnParser.h"
#include <cassert>
2022-03-29 21:10:50 +02:00
#include <memory>
2013-11-03 12:27:13 +02:00
#include <stack>
2022-03-29 21:10:50 +02:00
#include <utility>
#include <vector>
2013-11-03 12:27:13 +02:00
2020-02-01 23:06:01 +01:00
#include "cmsys/FStream.hxx"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmVisualStudioSlnData.h"
2016-07-09 11:21:54 +02:00
namespace {
enum LineFormat
2013-11-03 12:27:13 +02:00
{
2016-07-09 11:21:54 +02:00
LineMultiValueTag,
LineSingleValueTag,
LineKeyValuePair,
LineVerbatim
};
2013-11-03 12:27:13 +02:00
}
class cmVisualStudioSlnParser::ParsedLine
{
public:
bool IsComment() const;
bool IsKeyValuePair() const;
const std::string& GetTag() const { return this->Tag; }
const std::string& GetArg() const { return this->Arg.first; }
std::string GetArgVerbatim() const;
size_t GetValueCount() const { return this->Values.size(); }
const std::string& GetValue(size_t idxValue) const;
std::string GetValueVerbatim(size_t idxValue) const;
void SetTag(const std::string& tag) { this->Tag = tag; }
void SetArg(const std::string& arg) { this->Arg = StringData(arg, false); }
void SetQuotedArg(const std::string& arg)
2016-07-09 11:21:54 +02:00
{
this->Arg = StringData(arg, true);
}
2013-11-03 12:27:13 +02:00
void AddValue(const std::string& value)
2016-07-09 11:21:54 +02:00
{
this->Values.push_back(StringData(value, false));
}
2013-11-03 12:27:13 +02:00
void AddQuotedValue(const std::string& value)
2016-07-09 11:21:54 +02:00
{
this->Values.push_back(StringData(value, true));
}
2013-11-03 12:27:13 +02:00
void CopyVerbatim(const std::string& line) { this->Tag = line; }
private:
2020-02-01 23:06:01 +01:00
using StringData = std::pair<std::string, bool>;
2013-11-03 12:27:13 +02:00
std::string Tag;
StringData Arg;
std::vector<StringData> Values;
static const std::string BadString;
static const std::string Quote;
};
const std::string cmVisualStudioSlnParser::ParsedLine::BadString;
const std::string cmVisualStudioSlnParser::ParsedLine::Quote("\"");
bool cmVisualStudioSlnParser::ParsedLine::IsComment() const
{
assert(!this->Tag.empty());
2016-07-09 11:21:54 +02:00
return (this->Tag[0] == '#');
2013-11-03 12:27:13 +02:00
}
bool cmVisualStudioSlnParser::ParsedLine::IsKeyValuePair() const
{
assert(!this->Tag.empty());
return this->Arg.first.empty() && this->Values.size() == 1;
}
std::string cmVisualStudioSlnParser::ParsedLine::GetArgVerbatim() const
{
2023-05-23 16:38:00 +02:00
if (this->Arg.second) {
2013-11-03 12:27:13 +02:00
return Quote + this->Arg.first + Quote;
2023-05-23 16:38:00 +02:00
}
return this->Arg.first;
2013-11-03 12:27:13 +02:00
}
2016-07-09 11:21:54 +02:00
const std::string& cmVisualStudioSlnParser::ParsedLine::GetValue(
size_t idxValue) const
2013-11-03 12:27:13 +02:00
{
2023-05-23 16:38:00 +02:00
if (idxValue < this->Values.size()) {
2013-11-03 12:27:13 +02:00
return this->Values[idxValue].first;
2023-05-23 16:38:00 +02:00
}
return BadString;
2013-11-03 12:27:13 +02:00
}
2016-07-09 11:21:54 +02:00
std::string cmVisualStudioSlnParser::ParsedLine::GetValueVerbatim(
size_t idxValue) const
2013-11-03 12:27:13 +02:00
{
2016-07-09 11:21:54 +02:00
if (idxValue < this->Values.size()) {
2013-11-03 12:27:13 +02:00
const StringData& data = this->Values[idxValue];
2023-05-23 16:38:00 +02:00
if (data.second) {
2013-11-03 12:27:13 +02:00
return Quote + data.first + Quote;
2023-05-23 16:38:00 +02:00
}
return data.first;
}
return BadString;
2013-11-03 12:27:13 +02:00
}
class cmVisualStudioSlnParser::State
{
public:
explicit State(DataGroupSet requestedData);
size_t GetCurrentLine() const { return this->CurrentLine; }
bool ReadLine(std::istream& input, std::string& line);
LineFormat NextLineFormat() const;
bool Process(const cmVisualStudioSlnParser::ParsedLine& line,
2016-07-09 11:21:54 +02:00
cmSlnData& output, cmVisualStudioSlnParser::ResultData& result);
2013-11-03 12:27:13 +02:00
bool Finished(cmVisualStudioSlnParser::ResultData& result);
private:
enum FileState
{
FileStateStart,
FileStateTopLevel,
FileStateProject,
FileStateProjectDependencies,
FileStateGlobal,
FileStateSolutionConfigurations,
FileStateProjectConfigurations,
FileStateSolutionFilters,
FileStateGlobalSection,
FileStateIgnore
};
std::stack<FileState> Stack;
std::string EndIgnoreTag;
DataGroupSet RequestedData;
2023-05-23 16:38:00 +02:00
size_t CurrentLine = 0;
2013-11-03 12:27:13 +02:00
void IgnoreUntilTag(const std::string& endTag);
};
2016-07-09 11:21:54 +02:00
cmVisualStudioSlnParser::State::State(DataGroupSet requestedData)
: RequestedData(requestedData)
2013-11-03 12:27:13 +02:00
{
2023-05-23 16:38:00 +02:00
if (this->RequestedData.test(DataGroupProjectDependenciesBit)) {
2013-11-03 12:27:13 +02:00
this->RequestedData.set(DataGroupProjectsBit);
2023-05-23 16:38:00 +02:00
}
2013-11-03 12:27:13 +02:00
this->Stack.push(FileStateStart);
}
bool cmVisualStudioSlnParser::State::ReadLine(std::istream& input,
std::string& line)
{
++this->CurrentLine;
return !std::getline(input, line).fail();
}
LineFormat cmVisualStudioSlnParser::State::NextLineFormat() const
{
2016-07-09 11:21:54 +02:00
switch (this->Stack.top()) {
case FileStateStart:
return LineVerbatim;
case FileStateTopLevel:
return LineMultiValueTag;
case FileStateProject:
return LineSingleValueTag;
case FileStateProjectDependencies:
return LineKeyValuePair;
case FileStateGlobal:
return LineSingleValueTag;
case FileStateSolutionConfigurations:
return LineKeyValuePair;
case FileStateProjectConfigurations:
return LineKeyValuePair;
case FileStateSolutionFilters:
return LineKeyValuePair;
case FileStateGlobalSection:
return LineKeyValuePair;
case FileStateIgnore:
return LineVerbatim;
2013-11-03 12:27:13 +02:00
default:
assert(false);
return LineVerbatim;
2016-07-09 11:21:54 +02:00
}
2013-11-03 12:27:13 +02:00
}
bool cmVisualStudioSlnParser::State::Process(
2016-07-09 11:21:54 +02:00
const cmVisualStudioSlnParser::ParsedLine& line, cmSlnData& output,
cmVisualStudioSlnParser::ResultData& result)
2013-11-03 12:27:13 +02:00
{
assert(!line.IsComment());
2016-07-09 11:21:54 +02:00
switch (this->Stack.top()) {
2013-11-03 12:27:13 +02:00
case FileStateStart:
2020-02-01 23:06:01 +01:00
if (!cmHasLiteralPrefix(line.GetTag(),
"Microsoft Visual Studio Solution File")) {
2013-11-03 12:27:13 +02:00
result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
return false;
2016-07-09 11:21:54 +02:00
}
2013-11-03 12:27:13 +02:00
this->Stack.pop();
this->Stack.push(FileStateTopLevel);
break;
case FileStateTopLevel:
2023-05-23 16:38:00 +02:00
if (line.GetTag() == "Project") {
2016-07-09 11:21:54 +02:00
if (line.GetValueCount() != 3) {
2013-11-03 12:27:13 +02:00
result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
return false;
2016-07-09 11:21:54 +02:00
}
if (this->RequestedData.test(DataGroupProjectsBit)) {
if (!output.AddProject(line.GetValue(2), line.GetValue(0),
line.GetValue(1))) {
2013-11-03 12:27:13 +02:00
result.SetError(ResultErrorInputData, this->GetCurrentLine());
return false;
}
2016-07-09 11:21:54 +02:00
this->Stack.push(FileStateProject);
2023-05-23 16:38:00 +02:00
} else {
2013-11-03 12:27:13 +02:00
this->IgnoreUntilTag("EndProject");
2023-05-23 16:38:00 +02:00
}
} else if (line.GetTag() == "Global") {
2022-03-29 21:10:50 +02:00
2013-11-03 12:27:13 +02:00
this->Stack.push(FileStateGlobal);
2023-05-23 16:38:00 +02:00
} else if (line.GetTag() == "VisualStudioVersion") {
2022-03-29 21:10:50 +02:00
output.SetVisualStudioVersion(line.GetValue(0));
2023-05-23 16:38:00 +02:00
} else if (line.GetTag() == "MinimumVisualStudioVersion") {
2022-03-29 21:10:50 +02:00
output.SetMinimumVisualStudioVersion(line.GetValue(0));
} else {
2013-11-03 12:27:13 +02:00
result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
return false;
2016-07-09 11:21:54 +02:00
}
2013-11-03 12:27:13 +02:00
break;
case FileStateProject:
2023-05-23 16:38:00 +02:00
if (line.GetTag() == "EndProject") {
2013-11-03 12:27:13 +02:00
this->Stack.pop();
2023-05-23 16:38:00 +02:00
} else if (line.GetTag() == "ProjectSection") {
if (line.GetArg() == "ProjectDependencies" &&
line.GetValue(0) == "postProject") {
if (this->RequestedData.test(DataGroupProjectDependenciesBit)) {
2013-11-03 12:27:13 +02:00
this->Stack.push(FileStateProjectDependencies);
2023-05-23 16:38:00 +02:00
} else {
2013-11-03 12:27:13 +02:00
this->IgnoreUntilTag("EndProjectSection");
2023-05-23 16:38:00 +02:00
}
} else {
2013-11-03 12:27:13 +02:00
this->IgnoreUntilTag("EndProjectSection");
2023-05-23 16:38:00 +02:00
}
2016-07-09 11:21:54 +02:00
} else {
2013-11-03 12:27:13 +02:00
result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
return false;
2016-07-09 11:21:54 +02:00
}
2013-11-03 12:27:13 +02:00
break;
case FileStateProjectDependencies:
2023-05-23 16:38:00 +02:00
if (line.GetTag() == "EndProjectSection") {
2013-11-03 12:27:13 +02:00
this->Stack.pop();
2023-05-23 16:38:00 +02:00
} else if (line.IsKeyValuePair()) {
2013-11-03 12:27:13 +02:00
// implement dependency storing here, once needed
;
2023-05-23 16:38:00 +02:00
} else {
2013-11-03 12:27:13 +02:00
result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
return false;
2016-07-09 11:21:54 +02:00
}
2013-11-03 12:27:13 +02:00
break;
case FileStateGlobal:
2023-05-23 16:38:00 +02:00
if (line.GetTag() == "EndGlobal") {
2013-11-03 12:27:13 +02:00
this->Stack.pop();
2023-05-23 16:38:00 +02:00
} else if (line.GetTag() == "GlobalSection") {
if (line.GetArg() == "SolutionConfigurationPlatforms" &&
line.GetValue(0) == "preSolution") {
if (this->RequestedData.test(DataGroupSolutionConfigurationsBit)) {
2013-11-03 12:27:13 +02:00
this->Stack.push(FileStateSolutionConfigurations);
2023-05-23 16:38:00 +02:00
} else {
2013-11-03 12:27:13 +02:00
this->IgnoreUntilTag("EndGlobalSection");
2023-05-23 16:38:00 +02:00
}
} else if (line.GetArg() == "ProjectConfigurationPlatforms" &&
line.GetValue(0) == "postSolution") {
if (this->RequestedData.test(DataGroupProjectConfigurationsBit)) {
2013-11-03 12:27:13 +02:00
this->Stack.push(FileStateProjectConfigurations);
2023-05-23 16:38:00 +02:00
} else {
2013-11-03 12:27:13 +02:00
this->IgnoreUntilTag("EndGlobalSection");
2023-05-23 16:38:00 +02:00
}
} else if (line.GetArg() == "NestedProjects" &&
line.GetValue(0) == "preSolution") {
if (this->RequestedData.test(DataGroupSolutionFiltersBit)) {
2013-11-03 12:27:13 +02:00
this->Stack.push(FileStateSolutionFilters);
2023-05-23 16:38:00 +02:00
} else {
2013-11-03 12:27:13 +02:00
this->IgnoreUntilTag("EndGlobalSection");
2023-05-23 16:38:00 +02:00
}
} else if (this->RequestedData.test(
DataGroupGenericGlobalSectionsBit)) {
2013-11-03 12:27:13 +02:00
this->Stack.push(FileStateGlobalSection);
2023-05-23 16:38:00 +02:00
} else {
2013-11-03 12:27:13 +02:00
this->IgnoreUntilTag("EndGlobalSection");
2023-05-23 16:38:00 +02:00
}
2016-07-09 11:21:54 +02:00
} else {
2013-11-03 12:27:13 +02:00
result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
return false;
2016-07-09 11:21:54 +02:00
}
2013-11-03 12:27:13 +02:00
break;
case FileStateSolutionConfigurations:
2023-05-23 16:38:00 +02:00
if (line.GetTag() == "EndGlobalSection") {
2013-11-03 12:27:13 +02:00
this->Stack.pop();
2023-05-23 16:38:00 +02:00
} else if (line.IsKeyValuePair()) {
2022-03-29 21:10:50 +02:00
output.AddConfiguration(line.GetValue(0));
} else {
2013-11-03 12:27:13 +02:00
result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
return false;
2016-07-09 11:21:54 +02:00
}
2013-11-03 12:27:13 +02:00
break;
case FileStateProjectConfigurations:
2023-05-23 16:38:00 +02:00
if (line.GetTag() == "EndGlobalSection") {
2013-11-03 12:27:13 +02:00
this->Stack.pop();
2023-05-23 16:38:00 +02:00
} else if (line.IsKeyValuePair()) {
2022-03-29 21:10:50 +02:00
std::vector<std::string> tagElements =
cmSystemTools::SplitString(line.GetTag(), '.');
if (tagElements.size() != 3 && tagElements.size() != 4) {
result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
return false;
}
std::string guid = tagElements[0];
std::string solutionConfiguration = tagElements[1];
std::string activeBuild = tagElements[2];
cm::optional<cmSlnProjectEntry> projectEntry =
output.GetProjectByGUID(guid);
if (!projectEntry) {
result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
return false;
}
2023-05-23 16:38:00 +02:00
if (activeBuild == "ActiveCfg") {
2022-03-29 21:10:50 +02:00
projectEntry->AddProjectConfiguration(solutionConfiguration,
line.GetValue(0));
}
} else {
2013-11-03 12:27:13 +02:00
result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
return false;
2016-07-09 11:21:54 +02:00
}
2013-11-03 12:27:13 +02:00
break;
case FileStateSolutionFilters:
2023-05-23 16:38:00 +02:00
if (line.GetTag() == "EndGlobalSection") {
2013-11-03 12:27:13 +02:00
this->Stack.pop();
2023-05-23 16:38:00 +02:00
} else if (line.IsKeyValuePair()) {
2013-11-03 12:27:13 +02:00
// implement filter storing here, once needed
;
2023-05-23 16:38:00 +02:00
} else {
2013-11-03 12:27:13 +02:00
result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
return false;
2016-07-09 11:21:54 +02:00
}
2013-11-03 12:27:13 +02:00
break;
case FileStateGlobalSection:
2023-05-23 16:38:00 +02:00
if (line.GetTag() == "EndGlobalSection") {
2013-11-03 12:27:13 +02:00
this->Stack.pop();
2023-05-23 16:38:00 +02:00
} else if (line.IsKeyValuePair()) {
2013-11-03 12:27:13 +02:00
// implement section storing here, once needed
;
2023-05-23 16:38:00 +02:00
} else {
2013-11-03 12:27:13 +02:00
result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
return false;
2016-07-09 11:21:54 +02:00
}
2013-11-03 12:27:13 +02:00
break;
case FileStateIgnore:
2016-07-09 11:21:54 +02:00
if (line.GetTag() == this->EndIgnoreTag) {
2013-11-03 12:27:13 +02:00
this->Stack.pop();
2018-01-26 17:06:56 +01:00
this->EndIgnoreTag.clear();
2016-07-09 11:21:54 +02:00
}
2013-11-03 12:27:13 +02:00
break;
default:
result.SetError(ResultErrorBadInternalState, this->GetCurrentLine());
return false;
2016-07-09 11:21:54 +02:00
}
2013-11-03 12:27:13 +02:00
return true;
}
bool cmVisualStudioSlnParser::State::Finished(
cmVisualStudioSlnParser::ResultData& result)
{
2016-07-09 11:21:54 +02:00
if (this->Stack.top() != FileStateTopLevel) {
2013-11-03 12:27:13 +02:00
result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
return false;
2016-07-09 11:21:54 +02:00
}
2013-11-03 12:27:13 +02:00
result.Result = ResultOK;
return true;
}
void cmVisualStudioSlnParser::State::IgnoreUntilTag(const std::string& endTag)
{
this->Stack.push(FileStateIgnore);
this->EndIgnoreTag = endTag;
}
2023-05-23 16:38:00 +02:00
cmVisualStudioSlnParser::ResultData::ResultData() = default;
2013-11-03 12:27:13 +02:00
void cmVisualStudioSlnParser::ResultData::Clear()
{
*this = ResultData();
}
void cmVisualStudioSlnParser::ResultData::SetError(ParseResult error,
size_t line)
{
this->Result = error;
this->ResultLine = line;
}
const cmVisualStudioSlnParser::DataGroupSet
2016-07-09 11:21:54 +02:00
cmVisualStudioSlnParser::DataGroupProjects(
1 << cmVisualStudioSlnParser::DataGroupProjectsBit);
2013-11-03 12:27:13 +02:00
const cmVisualStudioSlnParser::DataGroupSet
2016-07-09 11:21:54 +02:00
cmVisualStudioSlnParser::DataGroupProjectDependencies(
1 << cmVisualStudioSlnParser::DataGroupProjectDependenciesBit);
2013-11-03 12:27:13 +02:00
const cmVisualStudioSlnParser::DataGroupSet
2016-07-09 11:21:54 +02:00
cmVisualStudioSlnParser::DataGroupSolutionConfigurations(
1 << cmVisualStudioSlnParser::DataGroupSolutionConfigurationsBit);
2013-11-03 12:27:13 +02:00
const cmVisualStudioSlnParser::DataGroupSet
2016-07-09 11:21:54 +02:00
cmVisualStudioSlnParser::DataGroupProjectConfigurations(
1 << cmVisualStudioSlnParser::DataGroupProjectConfigurationsBit);
2013-11-03 12:27:13 +02:00
const cmVisualStudioSlnParser::DataGroupSet
2016-07-09 11:21:54 +02:00
cmVisualStudioSlnParser::DataGroupSolutionFilters(
1 << cmVisualStudioSlnParser::DataGroupSolutionFiltersBit);
2013-11-03 12:27:13 +02:00
const cmVisualStudioSlnParser::DataGroupSet
2016-07-09 11:21:54 +02:00
cmVisualStudioSlnParser::DataGroupGenericGlobalSections(
1 << cmVisualStudioSlnParser::DataGroupGenericGlobalSectionsBit);
2013-11-03 12:27:13 +02:00
const cmVisualStudioSlnParser::DataGroupSet
2016-07-09 11:21:54 +02:00
cmVisualStudioSlnParser::DataGroupAll(~0);
2013-11-03 12:27:13 +02:00
2016-07-09 11:21:54 +02:00
bool cmVisualStudioSlnParser::Parse(std::istream& input, cmSlnData& output,
2013-11-03 12:27:13 +02:00
DataGroupSet dataGroups)
{
this->LastResult.Clear();
2016-07-09 11:21:54 +02:00
if (!this->IsDataGroupSetSupported(dataGroups)) {
2013-11-03 12:27:13 +02:00
this->LastResult.SetError(ResultErrorUnsupportedDataGroup, 0);
return false;
2016-07-09 11:21:54 +02:00
}
2013-11-03 12:27:13 +02:00
State state(dataGroups);
return this->ParseImpl(input, output, state);
}
bool cmVisualStudioSlnParser::ParseFile(const std::string& file,
cmSlnData& output,
DataGroupSet dataGroups)
{
this->LastResult.Clear();
2016-07-09 11:21:54 +02:00
if (!this->IsDataGroupSetSupported(dataGroups)) {
2013-11-03 12:27:13 +02:00
this->LastResult.SetError(ResultErrorUnsupportedDataGroup, 0);
return false;
2016-07-09 11:21:54 +02:00
}
2014-08-03 19:52:23 +02:00
cmsys::ifstream f(file.c_str());
2016-07-09 11:21:54 +02:00
if (!f) {
2013-11-03 12:27:13 +02:00
this->LastResult.SetError(ResultErrorOpeningInput, 0);
return false;
2016-07-09 11:21:54 +02:00
}
2013-11-03 12:27:13 +02:00
State state(dataGroups);
return this->ParseImpl(f, output, state);
}
2016-07-09 11:21:54 +02:00
cmVisualStudioSlnParser::ParseResult cmVisualStudioSlnParser::GetParseResult()
const
2013-11-03 12:27:13 +02:00
{
return this->LastResult.Result;
}
size_t cmVisualStudioSlnParser::GetParseResultLine() const
{
return this->LastResult.ResultLine;
}
bool cmVisualStudioSlnParser::GetParseHadBOM() const
{
return this->LastResult.HadBOM;
}
2016-07-09 11:21:54 +02:00
bool cmVisualStudioSlnParser::IsDataGroupSetSupported(
DataGroupSet dataGroups) const
2013-11-03 12:27:13 +02:00
{
2022-03-29 21:10:50 +02:00
return (dataGroups & DataGroupProjects) != 0;
2013-11-03 12:27:13 +02:00
}
2016-07-09 11:21:54 +02:00
bool cmVisualStudioSlnParser::ParseImpl(std::istream& input, cmSlnData& output,
2013-11-03 12:27:13 +02:00
State& state)
{
std::string line;
// Does the .sln start with a Byte Order Mark?
2023-05-23 16:38:00 +02:00
if (!this->ParseBOM(input, line, state)) {
2013-11-03 12:27:13 +02:00
return false;
2023-05-23 16:38:00 +02:00
}
2016-07-09 11:21:54 +02:00
do {
2020-02-01 23:06:01 +01:00
line = cmTrimWhitespace(line);
2023-05-23 16:38:00 +02:00
if (line.empty()) {
2013-11-03 12:27:13 +02:00
continue;
2023-05-23 16:38:00 +02:00
}
2013-11-03 12:27:13 +02:00
ParsedLine parsedLine;
2016-07-09 11:21:54 +02:00
switch (state.NextLineFormat()) {
2013-11-03 12:27:13 +02:00
case LineMultiValueTag:
2023-05-23 16:38:00 +02:00
if (!this->ParseMultiValueTag(line, parsedLine, state)) {
2013-11-03 12:27:13 +02:00
return false;
2023-05-23 16:38:00 +02:00
}
2013-11-03 12:27:13 +02:00
break;
case LineSingleValueTag:
2023-05-23 16:38:00 +02:00
if (!this->ParseSingleValueTag(line, parsedLine, state)) {
2013-11-03 12:27:13 +02:00
return false;
2023-05-23 16:38:00 +02:00
}
2013-11-03 12:27:13 +02:00
break;
case LineKeyValuePair:
2023-05-23 16:38:00 +02:00
if (!this->ParseKeyValuePair(line, parsedLine, state)) {
2013-11-03 12:27:13 +02:00
return false;
2023-05-23 16:38:00 +02:00
}
2013-11-03 12:27:13 +02:00
break;
case LineVerbatim:
parsedLine.CopyVerbatim(line);
break;
2016-07-09 11:21:54 +02:00
}
2023-05-23 16:38:00 +02:00
if (parsedLine.IsComment()) {
2013-11-03 12:27:13 +02:00
continue;
2023-05-23 16:38:00 +02:00
}
if (!state.Process(parsedLine, output, this->LastResult)) {
2013-11-03 12:27:13 +02:00
return false;
2023-05-23 16:38:00 +02:00
}
2016-07-09 11:21:54 +02:00
} while (state.ReadLine(input, line));
2013-11-03 12:27:13 +02:00
return state.Finished(this->LastResult);
}
2016-07-09 11:21:54 +02:00
bool cmVisualStudioSlnParser::ParseBOM(std::istream& input, std::string& line,
2013-11-03 12:27:13 +02:00
State& state)
{
char bom[4];
2016-07-09 11:21:54 +02:00
if (!input.get(bom, 4)) {
2013-11-03 12:27:13 +02:00
this->LastResult.SetError(ResultErrorReadingInput, 1);
return false;
2016-07-09 11:21:54 +02:00
}
2013-11-03 12:27:13 +02:00
this->LastResult.HadBOM =
(bom[0] == char(0xEF) && bom[1] == char(0xBB) && bom[2] == char(0xBF));
2016-07-09 11:21:54 +02:00
if (!state.ReadLine(input, line)) {
2013-11-03 12:27:13 +02:00
this->LastResult.SetError(ResultErrorReadingInput, 1);
return false;
2016-07-09 11:21:54 +02:00
}
2023-05-23 16:38:00 +02:00
if (!this->LastResult.HadBOM) {
2016-07-09 11:21:54 +02:00
line = bom + line; // it wasn't a BOM, prepend it to first line
2023-05-23 16:38:00 +02:00
}
2013-11-03 12:27:13 +02:00
return true;
}
bool cmVisualStudioSlnParser::ParseMultiValueTag(const std::string& line,
ParsedLine& parsedLine,
State& state)
{
size_t idxEqualSign = line.find('=');
2020-08-30 11:54:41 +02:00
auto fullTag = cm::string_view(line).substr(0, idxEqualSign);
2023-05-23 16:38:00 +02:00
if (!this->ParseTag(fullTag, parsedLine, state)) {
2013-11-03 12:27:13 +02:00
return false;
2023-05-23 16:38:00 +02:00
}
if (idxEqualSign != std::string::npos) {
2013-11-03 12:27:13 +02:00
size_t idxFieldStart = idxEqualSign + 1;
2016-07-09 11:21:54 +02:00
if (idxFieldStart < line.size()) {
2013-11-03 12:27:13 +02:00
size_t idxParsing = idxFieldStart;
bool inQuotes = false;
2016-07-09 11:21:54 +02:00
for (;;) {
2013-11-03 12:27:13 +02:00
idxParsing = line.find_first_of(",\"", idxParsing);
bool fieldOver = false;
2023-05-23 16:38:00 +02:00
if (idxParsing == std::string::npos) {
2013-11-03 12:27:13 +02:00
fieldOver = true;
2016-07-09 11:21:54 +02:00
if (inQuotes) {
2013-11-03 12:27:13 +02:00
this->LastResult.SetError(ResultErrorInputStructure,
state.GetCurrentLine());
return false;
}
2023-05-23 16:38:00 +02:00
} else if (line[idxParsing] == ',' && !inQuotes) {
2013-11-03 12:27:13 +02:00
fieldOver = true;
2023-05-23 16:38:00 +02:00
} else if (line[idxParsing] == '"') {
2013-11-03 12:27:13 +02:00
inQuotes = !inQuotes;
2023-05-23 16:38:00 +02:00
}
2016-07-09 11:21:54 +02:00
if (fieldOver) {
if (!this->ParseValue(
line.substr(idxFieldStart, idxParsing - idxFieldStart),
2023-05-23 16:38:00 +02:00
parsedLine)) {
2013-11-03 12:27:13 +02:00
return false;
2023-05-23 16:38:00 +02:00
}
if (idxParsing == std::string::npos) {
2016-07-09 11:21:54 +02:00
break; // end of last field
2023-05-23 16:38:00 +02:00
}
2013-11-03 12:27:13 +02:00
idxFieldStart = idxParsing + 1;
}
2016-07-09 11:21:54 +02:00
++idxParsing;
2013-11-03 12:27:13 +02:00
}
}
2016-07-09 11:21:54 +02:00
}
2013-11-03 12:27:13 +02:00
return true;
}
bool cmVisualStudioSlnParser::ParseSingleValueTag(const std::string& line,
ParsedLine& parsedLine,
State& state)
{
size_t idxEqualSign = line.find('=');
2020-08-30 11:54:41 +02:00
auto fullTag = cm::string_view(line).substr(0, idxEqualSign);
2023-05-23 16:38:00 +02:00
if (!this->ParseTag(fullTag, parsedLine, state)) {
2013-11-03 12:27:13 +02:00
return false;
2023-05-23 16:38:00 +02:00
}
if (idxEqualSign != std::string::npos) {
if (!this->ParseValue(line.substr(idxEqualSign + 1), parsedLine)) {
2013-11-03 12:27:13 +02:00
return false;
2023-05-23 16:38:00 +02:00
}
2016-07-09 11:21:54 +02:00
}
2013-11-03 12:27:13 +02:00
return true;
}
bool cmVisualStudioSlnParser::ParseKeyValuePair(const std::string& line,
ParsedLine& parsedLine,
State& /*state*/)
{
size_t idxEqualSign = line.find('=');
2023-05-23 16:38:00 +02:00
if (idxEqualSign == std::string::npos) {
2013-11-03 12:27:13 +02:00
parsedLine.CopyVerbatim(line);
return true;
2016-07-09 11:21:54 +02:00
}
2013-11-03 12:27:13 +02:00
const std::string& key = line.substr(0, idxEqualSign);
2020-02-01 23:06:01 +01:00
parsedLine.SetTag(cmTrimWhitespace(key));
2013-11-03 12:27:13 +02:00
const std::string& value = line.substr(idxEqualSign + 1);
2020-02-01 23:06:01 +01:00
parsedLine.AddValue(cmTrimWhitespace(value));
2013-11-03 12:27:13 +02:00
return true;
}
2020-08-30 11:54:41 +02:00
bool cmVisualStudioSlnParser::ParseTag(cm::string_view fullTag,
2016-07-09 11:21:54 +02:00
ParsedLine& parsedLine, State& state)
2013-11-03 12:27:13 +02:00
{
size_t idxLeftParen = fullTag.find('(');
2020-08-30 11:54:41 +02:00
if (idxLeftParen == cm::string_view::npos) {
2020-02-01 23:06:01 +01:00
parsedLine.SetTag(cmTrimWhitespace(fullTag));
2013-11-03 12:27:13 +02:00
return true;
2016-07-09 11:21:54 +02:00
}
2020-02-01 23:06:01 +01:00
parsedLine.SetTag(cmTrimWhitespace(fullTag.substr(0, idxLeftParen)));
2013-11-03 12:27:13 +02:00
size_t idxRightParen = fullTag.rfind(')');
2020-08-30 11:54:41 +02:00
if (idxRightParen == cm::string_view::npos) {
2013-11-03 12:27:13 +02:00
this->LastResult.SetError(ResultErrorInputStructure,
state.GetCurrentLine());
return false;
2016-07-09 11:21:54 +02:00
}
2020-02-01 23:06:01 +01:00
const std::string& arg = cmTrimWhitespace(
2013-11-03 12:27:13 +02:00
fullTag.substr(idxLeftParen + 1, idxRightParen - idxLeftParen - 1));
2019-11-11 23:01:05 +01:00
if (arg.front() == '"') {
if (arg.back() != '"') {
2013-11-03 12:27:13 +02:00
this->LastResult.SetError(ResultErrorInputStructure,
state.GetCurrentLine());
return false;
}
2016-07-09 11:21:54 +02:00
parsedLine.SetQuotedArg(arg.substr(1, arg.size() - 2));
2023-05-23 16:38:00 +02:00
} else {
2013-11-03 12:27:13 +02:00
parsedLine.SetArg(arg);
2023-05-23 16:38:00 +02:00
}
2013-11-03 12:27:13 +02:00
return true;
}
bool cmVisualStudioSlnParser::ParseValue(const std::string& value,
ParsedLine& parsedLine)
{
2020-02-01 23:06:01 +01:00
const std::string& trimmed = cmTrimWhitespace(value);
2023-05-23 16:38:00 +02:00
if (trimmed.empty()) {
2013-11-03 12:27:13 +02:00
parsedLine.AddValue(trimmed);
2023-05-23 16:38:00 +02:00
} else if (trimmed.front() == '"' && trimmed.back() == '"') {
2013-11-03 12:27:13 +02:00
parsedLine.AddQuotedValue(trimmed.substr(1, trimmed.size() - 2));
2023-05-23 16:38:00 +02:00
} else {
2013-11-03 12:27:13 +02:00
parsedLine.AddValue(trimmed);
2023-05-23 16:38:00 +02:00
}
2013-11-03 12:27:13 +02:00
return true;
}