| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713 |
- /*============================================================================
- CMake - Cross Platform Makefile Generator
- Copyright 2000-2013 Kitware, Inc., Insight Software Consortium
- Distributed under the OSI-approved BSD License (the "License");
- see accompanying file Copyright.txt for details.
- This software is distributed WITHOUT ANY WARRANTY; without even the
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the License for more information.
- ============================================================================*/
- #include "cmVisualStudioSlnParser.h"
- #include "cmSystemTools.h"
- #include "cmVisualStudioSlnData.h"
- #include <cmsys/FStream.hxx>
- #include <cassert>
- #include <stack>
- //----------------------------------------------------------------------------
- namespace
- {
- enum LineFormat
- {
- LineMultiValueTag,
- LineSingleValueTag,
- LineKeyValuePair,
- LineVerbatim
- };
- }
- //----------------------------------------------------------------------------
- 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)
- { this->Arg = StringData(arg, true); }
- void AddValue(const std::string& value)
- { this->Values.push_back(StringData(value, false)); }
- void AddQuotedValue(const std::string& value)
- { this->Values.push_back(StringData(value, true)); }
- void CopyVerbatim(const std::string& line) { this->Tag = line; }
- private:
- typedef std::pair<std::string, bool> StringData;
- 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());
- return (this->Tag[0]== '#');
- }
- //----------------------------------------------------------------------------
- bool cmVisualStudioSlnParser::ParsedLine::IsKeyValuePair() const
- {
- assert(!this->Tag.empty());
- return this->Arg.first.empty() && this->Values.size() == 1;
- }
- //----------------------------------------------------------------------------
- std::string cmVisualStudioSlnParser::ParsedLine::GetArgVerbatim() const
- {
- if (this->Arg.second)
- return Quote + this->Arg.first + Quote;
- else
- return this->Arg.first;
- }
- //----------------------------------------------------------------------------
- const std::string&
- cmVisualStudioSlnParser::ParsedLine::GetValue(size_t idxValue) const
- {
- if (idxValue < this->Values.size())
- return this->Values[idxValue].first;
- else
- return BadString;
- }
- //----------------------------------------------------------------------------
- std::string
- cmVisualStudioSlnParser::ParsedLine::GetValueVerbatim(size_t idxValue) const
- {
- if (idxValue < this->Values.size())
- {
- const StringData& data = this->Values[idxValue];
- if (data.second)
- return Quote + data.first + Quote;
- else
- return data.first;
- }
- else
- return BadString;
- }
- //----------------------------------------------------------------------------
- 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,
- cmSlnData& output,
- cmVisualStudioSlnParser::ResultData& result);
- 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;
- size_t CurrentLine;
- void IgnoreUntilTag(const std::string& endTag);
- };
- //----------------------------------------------------------------------------
- cmVisualStudioSlnParser::State::State(DataGroupSet requestedData) :
- RequestedData(requestedData),
- CurrentLine(0)
- {
- if (this->RequestedData.test(DataGroupProjectDependenciesBit))
- this->RequestedData.set(DataGroupProjectsBit);
- 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
- {
- 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;
- default:
- assert(false);
- return LineVerbatim;
- }
- }
- //----------------------------------------------------------------------------
- bool cmVisualStudioSlnParser::State::Process(
- const cmVisualStudioSlnParser::ParsedLine& line,
- cmSlnData& output, cmVisualStudioSlnParser::ResultData& result)
- {
- assert(!line.IsComment());
- switch (this->Stack.top())
- {
- case FileStateStart:
- if (!cmSystemTools::StringStartsWith(
- line.GetTag().c_str(), "Microsoft Visual Studio Solution File"))
- {
- result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
- return false;
- }
- this->Stack.pop();
- this->Stack.push(FileStateTopLevel);
- break;
- case FileStateTopLevel:
- if (line.GetTag().compare("Project") == 0)
- {
- if (line.GetValueCount() != 3)
- {
- result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
- return false;
- }
- if (this->RequestedData.test(DataGroupProjectsBit))
- {
- if (!output.AddProject(line.GetValue(2),
- line.GetValue(0),
- line.GetValue(1)))
- {
- result.SetError(ResultErrorInputData, this->GetCurrentLine());
- return false;
- }
- this->Stack.push(FileStateProject);
- }
- else
- this->IgnoreUntilTag("EndProject");
- }
- else if (line.GetTag().compare("Global") == 0)
- this->Stack.push(FileStateGlobal);
- else
- {
- result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
- return false;
- }
- break;
- case FileStateProject:
- if (line.GetTag().compare("EndProject") == 0)
- this->Stack.pop();
- else if (line.GetTag().compare("ProjectSection") == 0)
- {
- if (line.GetArg().compare("ProjectDependencies") == 0 &&
- line.GetValue(0).compare("postProject") == 0)
- {
- if (this->RequestedData.test(DataGroupProjectDependenciesBit))
- this->Stack.push(FileStateProjectDependencies);
- else
- this->IgnoreUntilTag("EndProjectSection");
- }
- else
- this->IgnoreUntilTag("EndProjectSection");
- }
- else
- {
- result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
- return false;
- }
- break;
- case FileStateProjectDependencies:
- if (line.GetTag().compare("EndProjectSection") == 0)
- this->Stack.pop();
- else if (line.IsKeyValuePair())
- // implement dependency storing here, once needed
- ;
- else
- {
- result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
- return false;
- }
- break;
- case FileStateGlobal:
- if (line.GetTag().compare("EndGlobal") == 0)
- this->Stack.pop();
- else if (line.GetTag().compare("GlobalSection") == 0)
- {
- if (line.GetArg().compare("SolutionConfigurationPlatforms") == 0 &&
- line.GetValue(0).compare("preSolution") == 0)
- {
- if (this->RequestedData.test(DataGroupSolutionConfigurationsBit))
- this->Stack.push(FileStateSolutionConfigurations);
- else
- this->IgnoreUntilTag("EndGlobalSection");
- }
- else if (line.GetArg().compare("ProjectConfigurationPlatforms") == 0 &&
- line.GetValue(0).compare("postSolution") == 0)
- {
- if (this->RequestedData.test(DataGroupProjectConfigurationsBit))
- this->Stack.push(FileStateProjectConfigurations);
- else
- this->IgnoreUntilTag("EndGlobalSection");
- }
- else if (line.GetArg().compare("NestedProjects") == 0 &&
- line.GetValue(0).compare("preSolution") == 0)
- {
- if (this->RequestedData.test(DataGroupSolutionFiltersBit))
- this->Stack.push(FileStateSolutionFilters);
- else
- this->IgnoreUntilTag("EndGlobalSection");
- }
- else if (this->RequestedData.test(DataGroupGenericGlobalSectionsBit))
- this->Stack.push(FileStateGlobalSection);
- else
- this->IgnoreUntilTag("EndGlobalSection");
- }
- else
- {
- result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
- return false;
- }
- break;
- case FileStateSolutionConfigurations:
- if (line.GetTag().compare("EndGlobalSection") == 0)
- this->Stack.pop();
- else if (line.IsKeyValuePair())
- // implement configuration storing here, once needed
- ;
- else
- {
- result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
- return false;
- }
- break;
- case FileStateProjectConfigurations:
- if (line.GetTag().compare("EndGlobalSection") == 0)
- this->Stack.pop();
- else if (line.IsKeyValuePair())
- // implement configuration storing here, once needed
- ;
- else
- {
- result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
- return false;
- }
- break;
- case FileStateSolutionFilters:
- if (line.GetTag().compare("EndGlobalSection") == 0)
- this->Stack.pop();
- else if (line.IsKeyValuePair())
- // implement filter storing here, once needed
- ;
- else
- {
- result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
- return false;
- }
- break;
- case FileStateGlobalSection:
- if (line.GetTag().compare("EndGlobalSection") == 0)
- this->Stack.pop();
- else if (line.IsKeyValuePair())
- // implement section storing here, once needed
- ;
- else
- {
- result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
- return false;
- }
- break;
- case FileStateIgnore:
- if (line.GetTag() == this->EndIgnoreTag)
- {
- this->Stack.pop();
- this->EndIgnoreTag = "";
- }
- break;
- default:
- result.SetError(ResultErrorBadInternalState, this->GetCurrentLine());
- return false;
- }
- return true;
- }
- //----------------------------------------------------------------------------
- bool cmVisualStudioSlnParser::State::Finished(
- cmVisualStudioSlnParser::ResultData& result)
- {
- if (this->Stack.top() != FileStateTopLevel)
- {
- result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
- return false;
- }
- result.Result = ResultOK;
- return true;
- }
- //----------------------------------------------------------------------------
- void cmVisualStudioSlnParser::State::IgnoreUntilTag(const std::string& endTag)
- {
- this->Stack.push(FileStateIgnore);
- this->EndIgnoreTag = endTag;
- }
- //----------------------------------------------------------------------------
- cmVisualStudioSlnParser::ResultData::ResultData()
- : Result(ResultOK)
- , ResultLine(0)
- {}
- //----------------------------------------------------------------------------
- void cmVisualStudioSlnParser::ResultData::Clear()
- {
- *this = ResultData();
- }
- //----------------------------------------------------------------------------
- void cmVisualStudioSlnParser::ResultData::SetError(ParseResult error,
- size_t line)
- {
- this->Result = error;
- this->ResultLine = line;
- }
- //----------------------------------------------------------------------------
- const cmVisualStudioSlnParser::DataGroupSet
- cmVisualStudioSlnParser::DataGroupProjects(
- 1 << cmVisualStudioSlnParser::DataGroupProjectsBit);
- const cmVisualStudioSlnParser::DataGroupSet
- cmVisualStudioSlnParser::DataGroupProjectDependencies(
- 1 << cmVisualStudioSlnParser::DataGroupProjectDependenciesBit);
- const cmVisualStudioSlnParser::DataGroupSet
- cmVisualStudioSlnParser::DataGroupSolutionConfigurations(
- 1 << cmVisualStudioSlnParser::DataGroupSolutionConfigurationsBit);
- const cmVisualStudioSlnParser::DataGroupSet
- cmVisualStudioSlnParser::DataGroupProjectConfigurations(
- 1 << cmVisualStudioSlnParser::DataGroupProjectConfigurationsBit);
- const cmVisualStudioSlnParser::DataGroupSet
- cmVisualStudioSlnParser::DataGroupSolutionFilters(
- 1 << cmVisualStudioSlnParser::DataGroupSolutionFiltersBit);
- const cmVisualStudioSlnParser::DataGroupSet
- cmVisualStudioSlnParser::DataGroupGenericGlobalSections(
- 1 << cmVisualStudioSlnParser::DataGroupGenericGlobalSectionsBit);
- const cmVisualStudioSlnParser::DataGroupSet
- cmVisualStudioSlnParser::DataGroupAll(~0);
- //----------------------------------------------------------------------------
- bool cmVisualStudioSlnParser::Parse(std::istream& input,
- cmSlnData& output,
- DataGroupSet dataGroups)
- {
- this->LastResult.Clear();
- if (!this->IsDataGroupSetSupported(dataGroups))
- {
- this->LastResult.SetError(ResultErrorUnsupportedDataGroup, 0);
- return false;
- }
- State state(dataGroups);
- return this->ParseImpl(input, output, state);
- }
- //----------------------------------------------------------------------------
- bool cmVisualStudioSlnParser::ParseFile(const std::string& file,
- cmSlnData& output,
- DataGroupSet dataGroups)
- {
- this->LastResult.Clear();
- if (!this->IsDataGroupSetSupported(dataGroups))
- {
- this->LastResult.SetError(ResultErrorUnsupportedDataGroup, 0);
- return false;
- }
- cmsys::ifstream f(file.c_str());
- if (!f)
- {
- this->LastResult.SetError(ResultErrorOpeningInput, 0);
- return false;
- }
- State state(dataGroups);
- return this->ParseImpl(f, output, state);
- }
- //----------------------------------------------------------------------------
- cmVisualStudioSlnParser::ParseResult
- cmVisualStudioSlnParser::GetParseResult() const
- {
- return this->LastResult.Result;
- }
- //----------------------------------------------------------------------------
- size_t cmVisualStudioSlnParser::GetParseResultLine() const
- {
- return this->LastResult.ResultLine;
- }
- //----------------------------------------------------------------------------
- bool cmVisualStudioSlnParser::GetParseHadBOM() const
- {
- return this->LastResult.HadBOM;
- }
- //----------------------------------------------------------------------------
- bool
- cmVisualStudioSlnParser::IsDataGroupSetSupported(DataGroupSet dataGroups) const
- {
- return (dataGroups & DataGroupProjects) == dataGroups;
- //only supporting DataGroupProjects for now
- }
- //----------------------------------------------------------------------------
- bool cmVisualStudioSlnParser::ParseImpl(std::istream& input,
- cmSlnData& output,
- State& state)
- {
- std::string line;
- // Does the .sln start with a Byte Order Mark?
- if (!this->ParseBOM(input, line, state))
- return false;
- do
- {
- line = cmSystemTools::TrimWhitespace(line);
- if (line.empty())
- continue;
- ParsedLine parsedLine;
- switch (state.NextLineFormat())
- {
- case LineMultiValueTag:
- if (!this->ParseMultiValueTag(line, parsedLine, state))
- return false;
- break;
- case LineSingleValueTag:
- if (!this->ParseSingleValueTag(line, parsedLine, state))
- return false;
- break;
- case LineKeyValuePair:
- if (!this->ParseKeyValuePair(line, parsedLine, state))
- return false;
- break;
- case LineVerbatim:
- parsedLine.CopyVerbatim(line);
- break;
- }
- if (parsedLine.IsComment())
- continue;
- if (!state.Process(parsedLine, output, this->LastResult))
- return false;
- }
- while (state.ReadLine(input, line));
- return state.Finished(this->LastResult);
- }
- //----------------------------------------------------------------------------
- bool cmVisualStudioSlnParser::ParseBOM(std::istream& input,
- std::string& line,
- State& state)
- {
- char bom[4];
- if (!input.get(bom, 4))
- {
- this->LastResult.SetError(ResultErrorReadingInput, 1);
- return false;
- }
- this->LastResult.HadBOM =
- (bom[0] == char(0xEF) && bom[1] == char(0xBB) && bom[2] == char(0xBF));
- if (!state.ReadLine(input, line))
- {
- this->LastResult.SetError(ResultErrorReadingInput, 1);
- return false;
- }
- if (!this->LastResult.HadBOM)
- line = bom + line; // it wasn't a BOM, prepend it to first line
- return true;
- }
- //----------------------------------------------------------------------------
- bool cmVisualStudioSlnParser::ParseMultiValueTag(const std::string& line,
- ParsedLine& parsedLine,
- State& state)
- {
- size_t idxEqualSign = line.find('=');
- const std::string& fullTag = line.substr(0, idxEqualSign);
- if (!this->ParseTag(fullTag, parsedLine, state))
- return false;
- if (idxEqualSign != line.npos)
- {
- size_t idxFieldStart = idxEqualSign + 1;
- if (idxFieldStart < line.size())
- {
- size_t idxParsing = idxFieldStart;
- bool inQuotes = false;
- for (;;)
- {
- idxParsing = line.find_first_of(",\"", idxParsing);
- bool fieldOver = false;
- if (idxParsing == line.npos)
- {
- fieldOver = true;
- if (inQuotes)
- {
- this->LastResult.SetError(ResultErrorInputStructure,
- state.GetCurrentLine());
- return false;
- }
- }
- else if (line[idxParsing] == ',' && !inQuotes)
- fieldOver = true;
- else if (line[idxParsing] == '"')
- inQuotes = !inQuotes;
- if (fieldOver)
- {
- if (!this->ParseValue(line.substr(idxFieldStart,
- idxParsing - idxFieldStart),
- parsedLine))
- return false;
- if (idxParsing == line.npos)
- break; //end of last field
- idxFieldStart = idxParsing + 1;
- }
- ++idxParsing;
- }
- }
- }
- return true;
- }
- //----------------------------------------------------------------------------
- bool cmVisualStudioSlnParser::ParseSingleValueTag(const std::string& line,
- ParsedLine& parsedLine,
- State& state)
- {
- size_t idxEqualSign = line.find('=');
- const std::string& fullTag = line.substr(0, idxEqualSign);
- if (!this->ParseTag(fullTag, parsedLine, state))
- return false;
- if (idxEqualSign != line.npos)
- {
- if (!this->ParseValue(line.substr(idxEqualSign + 1), parsedLine))
- return false;
- }
- return true;
- }
- //----------------------------------------------------------------------------
- bool cmVisualStudioSlnParser::ParseKeyValuePair(const std::string& line,
- ParsedLine& parsedLine,
- State& /*state*/)
- {
- size_t idxEqualSign = line.find('=');
- if (idxEqualSign == line.npos)
- {
- parsedLine.CopyVerbatim(line);
- return true;
- }
- const std::string& key = line.substr(0, idxEqualSign);
- parsedLine.SetTag(cmSystemTools::TrimWhitespace(key));
- const std::string& value = line.substr(idxEqualSign + 1);
- parsedLine.AddValue(cmSystemTools::TrimWhitespace(value));
- return true;
- }
- //----------------------------------------------------------------------------
- bool cmVisualStudioSlnParser::ParseTag(const std::string& fullTag,
- ParsedLine& parsedLine,
- State& state)
- {
- size_t idxLeftParen = fullTag.find('(');
- if (idxLeftParen == fullTag.npos)
- {
- parsedLine.SetTag(cmSystemTools::TrimWhitespace(fullTag));
- return true;
- }
- parsedLine.SetTag(
- cmSystemTools::TrimWhitespace(fullTag.substr(0, idxLeftParen)));
- size_t idxRightParen = fullTag.rfind(')');
- if (idxRightParen == fullTag.npos)
- {
- this->LastResult.SetError(ResultErrorInputStructure,
- state.GetCurrentLine());
- return false;
- }
- const std::string& arg = cmSystemTools::TrimWhitespace(
- fullTag.substr(idxLeftParen + 1, idxRightParen - idxLeftParen - 1));
- if (arg[0] == '"')
- {
- if (arg[arg.size() - 1] != '"')
- {
- this->LastResult.SetError(ResultErrorInputStructure,
- state.GetCurrentLine());
- return false;
- }
- parsedLine.SetQuotedArg(arg.substr(1, arg.size() - 2));
- }
- else
- parsedLine.SetArg(arg);
- return true;
- }
- //----------------------------------------------------------------------------
- bool cmVisualStudioSlnParser::ParseValue(const std::string& value,
- ParsedLine& parsedLine)
- {
- const std::string& trimmed = cmSystemTools::TrimWhitespace(value);
- if (trimmed.empty())
- parsedLine.AddValue(trimmed);
- else if (trimmed[0] == '"' && trimmed[trimmed.size() - 1] == '"')
- parsedLine.AddQuotedValue(trimmed.substr(1, trimmed.size() - 2));
- else
- parsedLine.AddValue(trimmed);
- return true;
- }
|