cmVisualStudioSlnParser.cxx 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713
  1. /*============================================================================
  2. CMake - Cross Platform Makefile Generator
  3. Copyright 2000-2013 Kitware, Inc., Insight Software Consortium
  4. Distributed under the OSI-approved BSD License (the "License");
  5. see accompanying file Copyright.txt for details.
  6. This software is distributed WITHOUT ANY WARRANTY; without even the
  7. implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8. See the License for more information.
  9. ============================================================================*/
  10. #include "cmVisualStudioSlnParser.h"
  11. #include "cmSystemTools.h"
  12. #include "cmVisualStudioSlnData.h"
  13. #include <cmsys/FStream.hxx>
  14. #include <cassert>
  15. #include <stack>
  16. //----------------------------------------------------------------------------
  17. namespace
  18. {
  19. enum LineFormat
  20. {
  21. LineMultiValueTag,
  22. LineSingleValueTag,
  23. LineKeyValuePair,
  24. LineVerbatim
  25. };
  26. }
  27. //----------------------------------------------------------------------------
  28. class cmVisualStudioSlnParser::ParsedLine
  29. {
  30. public:
  31. bool IsComment() const;
  32. bool IsKeyValuePair() const;
  33. const std::string& GetTag() const { return this->Tag; }
  34. const std::string& GetArg() const { return this->Arg.first; }
  35. std::string GetArgVerbatim() const;
  36. size_t GetValueCount() const { return this->Values.size(); }
  37. const std::string& GetValue(size_t idxValue) const;
  38. std::string GetValueVerbatim(size_t idxValue) const;
  39. void SetTag(const std::string& tag) { this->Tag = tag; }
  40. void SetArg(const std::string& arg) { this->Arg = StringData(arg, false); }
  41. void SetQuotedArg(const std::string& arg)
  42. { this->Arg = StringData(arg, true); }
  43. void AddValue(const std::string& value)
  44. { this->Values.push_back(StringData(value, false)); }
  45. void AddQuotedValue(const std::string& value)
  46. { this->Values.push_back(StringData(value, true)); }
  47. void CopyVerbatim(const std::string& line) { this->Tag = line; }
  48. private:
  49. typedef std::pair<std::string, bool> StringData;
  50. std::string Tag;
  51. StringData Arg;
  52. std::vector<StringData> Values;
  53. static const std::string BadString;
  54. static const std::string Quote;
  55. };
  56. //----------------------------------------------------------------------------
  57. const std::string cmVisualStudioSlnParser::ParsedLine::BadString;
  58. const std::string cmVisualStudioSlnParser::ParsedLine::Quote("\"");
  59. //----------------------------------------------------------------------------
  60. bool cmVisualStudioSlnParser::ParsedLine::IsComment() const
  61. {
  62. assert(!this->Tag.empty());
  63. return (this->Tag[0]== '#');
  64. }
  65. //----------------------------------------------------------------------------
  66. bool cmVisualStudioSlnParser::ParsedLine::IsKeyValuePair() const
  67. {
  68. assert(!this->Tag.empty());
  69. return this->Arg.first.empty() && this->Values.size() == 1;
  70. }
  71. //----------------------------------------------------------------------------
  72. std::string cmVisualStudioSlnParser::ParsedLine::GetArgVerbatim() const
  73. {
  74. if (this->Arg.second)
  75. return Quote + this->Arg.first + Quote;
  76. else
  77. return this->Arg.first;
  78. }
  79. //----------------------------------------------------------------------------
  80. const std::string&
  81. cmVisualStudioSlnParser::ParsedLine::GetValue(size_t idxValue) const
  82. {
  83. if (idxValue < this->Values.size())
  84. return this->Values[idxValue].first;
  85. else
  86. return BadString;
  87. }
  88. //----------------------------------------------------------------------------
  89. std::string
  90. cmVisualStudioSlnParser::ParsedLine::GetValueVerbatim(size_t idxValue) const
  91. {
  92. if (idxValue < this->Values.size())
  93. {
  94. const StringData& data = this->Values[idxValue];
  95. if (data.second)
  96. return Quote + data.first + Quote;
  97. else
  98. return data.first;
  99. }
  100. else
  101. return BadString;
  102. }
  103. //----------------------------------------------------------------------------
  104. class cmVisualStudioSlnParser::State
  105. {
  106. public:
  107. explicit State(DataGroupSet requestedData);
  108. size_t GetCurrentLine() const { return this->CurrentLine; }
  109. bool ReadLine(std::istream& input, std::string& line);
  110. LineFormat NextLineFormat() const;
  111. bool Process(const cmVisualStudioSlnParser::ParsedLine& line,
  112. cmSlnData& output,
  113. cmVisualStudioSlnParser::ResultData& result);
  114. bool Finished(cmVisualStudioSlnParser::ResultData& result);
  115. private:
  116. enum FileState
  117. {
  118. FileStateStart,
  119. FileStateTopLevel,
  120. FileStateProject,
  121. FileStateProjectDependencies,
  122. FileStateGlobal,
  123. FileStateSolutionConfigurations,
  124. FileStateProjectConfigurations,
  125. FileStateSolutionFilters,
  126. FileStateGlobalSection,
  127. FileStateIgnore
  128. };
  129. std::stack<FileState> Stack;
  130. std::string EndIgnoreTag;
  131. DataGroupSet RequestedData;
  132. size_t CurrentLine;
  133. void IgnoreUntilTag(const std::string& endTag);
  134. };
  135. //----------------------------------------------------------------------------
  136. cmVisualStudioSlnParser::State::State(DataGroupSet requestedData) :
  137. RequestedData(requestedData),
  138. CurrentLine(0)
  139. {
  140. if (this->RequestedData.test(DataGroupProjectDependenciesBit))
  141. this->RequestedData.set(DataGroupProjectsBit);
  142. this->Stack.push(FileStateStart);
  143. }
  144. //----------------------------------------------------------------------------
  145. bool cmVisualStudioSlnParser::State::ReadLine(std::istream& input,
  146. std::string& line)
  147. {
  148. ++this->CurrentLine;
  149. return !std::getline(input, line).fail();
  150. }
  151. //----------------------------------------------------------------------------
  152. LineFormat cmVisualStudioSlnParser::State::NextLineFormat() const
  153. {
  154. switch (this->Stack.top())
  155. {
  156. case FileStateStart: return LineVerbatim;
  157. case FileStateTopLevel: return LineMultiValueTag;
  158. case FileStateProject: return LineSingleValueTag;
  159. case FileStateProjectDependencies: return LineKeyValuePair;
  160. case FileStateGlobal: return LineSingleValueTag;
  161. case FileStateSolutionConfigurations: return LineKeyValuePair;
  162. case FileStateProjectConfigurations: return LineKeyValuePair;
  163. case FileStateSolutionFilters: return LineKeyValuePair;
  164. case FileStateGlobalSection: return LineKeyValuePair;
  165. case FileStateIgnore: return LineVerbatim;
  166. default:
  167. assert(false);
  168. return LineVerbatim;
  169. }
  170. }
  171. //----------------------------------------------------------------------------
  172. bool cmVisualStudioSlnParser::State::Process(
  173. const cmVisualStudioSlnParser::ParsedLine& line,
  174. cmSlnData& output, cmVisualStudioSlnParser::ResultData& result)
  175. {
  176. assert(!line.IsComment());
  177. switch (this->Stack.top())
  178. {
  179. case FileStateStart:
  180. if (!cmSystemTools::StringStartsWith(
  181. line.GetTag().c_str(), "Microsoft Visual Studio Solution File"))
  182. {
  183. result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
  184. return false;
  185. }
  186. this->Stack.pop();
  187. this->Stack.push(FileStateTopLevel);
  188. break;
  189. case FileStateTopLevel:
  190. if (line.GetTag().compare("Project") == 0)
  191. {
  192. if (line.GetValueCount() != 3)
  193. {
  194. result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
  195. return false;
  196. }
  197. if (this->RequestedData.test(DataGroupProjectsBit))
  198. {
  199. if (!output.AddProject(line.GetValue(2),
  200. line.GetValue(0),
  201. line.GetValue(1)))
  202. {
  203. result.SetError(ResultErrorInputData, this->GetCurrentLine());
  204. return false;
  205. }
  206. this->Stack.push(FileStateProject);
  207. }
  208. else
  209. this->IgnoreUntilTag("EndProject");
  210. }
  211. else if (line.GetTag().compare("Global") == 0)
  212. this->Stack.push(FileStateGlobal);
  213. else
  214. {
  215. result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
  216. return false;
  217. }
  218. break;
  219. case FileStateProject:
  220. if (line.GetTag().compare("EndProject") == 0)
  221. this->Stack.pop();
  222. else if (line.GetTag().compare("ProjectSection") == 0)
  223. {
  224. if (line.GetArg().compare("ProjectDependencies") == 0 &&
  225. line.GetValue(0).compare("postProject") == 0)
  226. {
  227. if (this->RequestedData.test(DataGroupProjectDependenciesBit))
  228. this->Stack.push(FileStateProjectDependencies);
  229. else
  230. this->IgnoreUntilTag("EndProjectSection");
  231. }
  232. else
  233. this->IgnoreUntilTag("EndProjectSection");
  234. }
  235. else
  236. {
  237. result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
  238. return false;
  239. }
  240. break;
  241. case FileStateProjectDependencies:
  242. if (line.GetTag().compare("EndProjectSection") == 0)
  243. this->Stack.pop();
  244. else if (line.IsKeyValuePair())
  245. // implement dependency storing here, once needed
  246. ;
  247. else
  248. {
  249. result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
  250. return false;
  251. }
  252. break;
  253. case FileStateGlobal:
  254. if (line.GetTag().compare("EndGlobal") == 0)
  255. this->Stack.pop();
  256. else if (line.GetTag().compare("GlobalSection") == 0)
  257. {
  258. if (line.GetArg().compare("SolutionConfigurationPlatforms") == 0 &&
  259. line.GetValue(0).compare("preSolution") == 0)
  260. {
  261. if (this->RequestedData.test(DataGroupSolutionConfigurationsBit))
  262. this->Stack.push(FileStateSolutionConfigurations);
  263. else
  264. this->IgnoreUntilTag("EndGlobalSection");
  265. }
  266. else if (line.GetArg().compare("ProjectConfigurationPlatforms") == 0 &&
  267. line.GetValue(0).compare("postSolution") == 0)
  268. {
  269. if (this->RequestedData.test(DataGroupProjectConfigurationsBit))
  270. this->Stack.push(FileStateProjectConfigurations);
  271. else
  272. this->IgnoreUntilTag("EndGlobalSection");
  273. }
  274. else if (line.GetArg().compare("NestedProjects") == 0 &&
  275. line.GetValue(0).compare("preSolution") == 0)
  276. {
  277. if (this->RequestedData.test(DataGroupSolutionFiltersBit))
  278. this->Stack.push(FileStateSolutionFilters);
  279. else
  280. this->IgnoreUntilTag("EndGlobalSection");
  281. }
  282. else if (this->RequestedData.test(DataGroupGenericGlobalSectionsBit))
  283. this->Stack.push(FileStateGlobalSection);
  284. else
  285. this->IgnoreUntilTag("EndGlobalSection");
  286. }
  287. else
  288. {
  289. result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
  290. return false;
  291. }
  292. break;
  293. case FileStateSolutionConfigurations:
  294. if (line.GetTag().compare("EndGlobalSection") == 0)
  295. this->Stack.pop();
  296. else if (line.IsKeyValuePair())
  297. // implement configuration storing here, once needed
  298. ;
  299. else
  300. {
  301. result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
  302. return false;
  303. }
  304. break;
  305. case FileStateProjectConfigurations:
  306. if (line.GetTag().compare("EndGlobalSection") == 0)
  307. this->Stack.pop();
  308. else if (line.IsKeyValuePair())
  309. // implement configuration storing here, once needed
  310. ;
  311. else
  312. {
  313. result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
  314. return false;
  315. }
  316. break;
  317. case FileStateSolutionFilters:
  318. if (line.GetTag().compare("EndGlobalSection") == 0)
  319. this->Stack.pop();
  320. else if (line.IsKeyValuePair())
  321. // implement filter storing here, once needed
  322. ;
  323. else
  324. {
  325. result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
  326. return false;
  327. }
  328. break;
  329. case FileStateGlobalSection:
  330. if (line.GetTag().compare("EndGlobalSection") == 0)
  331. this->Stack.pop();
  332. else if (line.IsKeyValuePair())
  333. // implement section storing here, once needed
  334. ;
  335. else
  336. {
  337. result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
  338. return false;
  339. }
  340. break;
  341. case FileStateIgnore:
  342. if (line.GetTag() == this->EndIgnoreTag)
  343. {
  344. this->Stack.pop();
  345. this->EndIgnoreTag = "";
  346. }
  347. break;
  348. default:
  349. result.SetError(ResultErrorBadInternalState, this->GetCurrentLine());
  350. return false;
  351. }
  352. return true;
  353. }
  354. //----------------------------------------------------------------------------
  355. bool cmVisualStudioSlnParser::State::Finished(
  356. cmVisualStudioSlnParser::ResultData& result)
  357. {
  358. if (this->Stack.top() != FileStateTopLevel)
  359. {
  360. result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
  361. return false;
  362. }
  363. result.Result = ResultOK;
  364. return true;
  365. }
  366. //----------------------------------------------------------------------------
  367. void cmVisualStudioSlnParser::State::IgnoreUntilTag(const std::string& endTag)
  368. {
  369. this->Stack.push(FileStateIgnore);
  370. this->EndIgnoreTag = endTag;
  371. }
  372. //----------------------------------------------------------------------------
  373. cmVisualStudioSlnParser::ResultData::ResultData()
  374. : Result(ResultOK)
  375. , ResultLine(0)
  376. {}
  377. //----------------------------------------------------------------------------
  378. void cmVisualStudioSlnParser::ResultData::Clear()
  379. {
  380. *this = ResultData();
  381. }
  382. //----------------------------------------------------------------------------
  383. void cmVisualStudioSlnParser::ResultData::SetError(ParseResult error,
  384. size_t line)
  385. {
  386. this->Result = error;
  387. this->ResultLine = line;
  388. }
  389. //----------------------------------------------------------------------------
  390. const cmVisualStudioSlnParser::DataGroupSet
  391. cmVisualStudioSlnParser::DataGroupProjects(
  392. 1 << cmVisualStudioSlnParser::DataGroupProjectsBit);
  393. const cmVisualStudioSlnParser::DataGroupSet
  394. cmVisualStudioSlnParser::DataGroupProjectDependencies(
  395. 1 << cmVisualStudioSlnParser::DataGroupProjectDependenciesBit);
  396. const cmVisualStudioSlnParser::DataGroupSet
  397. cmVisualStudioSlnParser::DataGroupSolutionConfigurations(
  398. 1 << cmVisualStudioSlnParser::DataGroupSolutionConfigurationsBit);
  399. const cmVisualStudioSlnParser::DataGroupSet
  400. cmVisualStudioSlnParser::DataGroupProjectConfigurations(
  401. 1 << cmVisualStudioSlnParser::DataGroupProjectConfigurationsBit);
  402. const cmVisualStudioSlnParser::DataGroupSet
  403. cmVisualStudioSlnParser::DataGroupSolutionFilters(
  404. 1 << cmVisualStudioSlnParser::DataGroupSolutionFiltersBit);
  405. const cmVisualStudioSlnParser::DataGroupSet
  406. cmVisualStudioSlnParser::DataGroupGenericGlobalSections(
  407. 1 << cmVisualStudioSlnParser::DataGroupGenericGlobalSectionsBit);
  408. const cmVisualStudioSlnParser::DataGroupSet
  409. cmVisualStudioSlnParser::DataGroupAll(~0);
  410. //----------------------------------------------------------------------------
  411. bool cmVisualStudioSlnParser::Parse(std::istream& input,
  412. cmSlnData& output,
  413. DataGroupSet dataGroups)
  414. {
  415. this->LastResult.Clear();
  416. if (!this->IsDataGroupSetSupported(dataGroups))
  417. {
  418. this->LastResult.SetError(ResultErrorUnsupportedDataGroup, 0);
  419. return false;
  420. }
  421. State state(dataGroups);
  422. return this->ParseImpl(input, output, state);
  423. }
  424. //----------------------------------------------------------------------------
  425. bool cmVisualStudioSlnParser::ParseFile(const std::string& file,
  426. cmSlnData& output,
  427. DataGroupSet dataGroups)
  428. {
  429. this->LastResult.Clear();
  430. if (!this->IsDataGroupSetSupported(dataGroups))
  431. {
  432. this->LastResult.SetError(ResultErrorUnsupportedDataGroup, 0);
  433. return false;
  434. }
  435. cmsys::ifstream f(file.c_str());
  436. if (!f)
  437. {
  438. this->LastResult.SetError(ResultErrorOpeningInput, 0);
  439. return false;
  440. }
  441. State state(dataGroups);
  442. return this->ParseImpl(f, output, state);
  443. }
  444. //----------------------------------------------------------------------------
  445. cmVisualStudioSlnParser::ParseResult
  446. cmVisualStudioSlnParser::GetParseResult() const
  447. {
  448. return this->LastResult.Result;
  449. }
  450. //----------------------------------------------------------------------------
  451. size_t cmVisualStudioSlnParser::GetParseResultLine() const
  452. {
  453. return this->LastResult.ResultLine;
  454. }
  455. //----------------------------------------------------------------------------
  456. bool cmVisualStudioSlnParser::GetParseHadBOM() const
  457. {
  458. return this->LastResult.HadBOM;
  459. }
  460. //----------------------------------------------------------------------------
  461. bool
  462. cmVisualStudioSlnParser::IsDataGroupSetSupported(DataGroupSet dataGroups) const
  463. {
  464. return (dataGroups & DataGroupProjects) == dataGroups;
  465. //only supporting DataGroupProjects for now
  466. }
  467. //----------------------------------------------------------------------------
  468. bool cmVisualStudioSlnParser::ParseImpl(std::istream& input,
  469. cmSlnData& output,
  470. State& state)
  471. {
  472. std::string line;
  473. // Does the .sln start with a Byte Order Mark?
  474. if (!this->ParseBOM(input, line, state))
  475. return false;
  476. do
  477. {
  478. line = cmSystemTools::TrimWhitespace(line);
  479. if (line.empty())
  480. continue;
  481. ParsedLine parsedLine;
  482. switch (state.NextLineFormat())
  483. {
  484. case LineMultiValueTag:
  485. if (!this->ParseMultiValueTag(line, parsedLine, state))
  486. return false;
  487. break;
  488. case LineSingleValueTag:
  489. if (!this->ParseSingleValueTag(line, parsedLine, state))
  490. return false;
  491. break;
  492. case LineKeyValuePair:
  493. if (!this->ParseKeyValuePair(line, parsedLine, state))
  494. return false;
  495. break;
  496. case LineVerbatim:
  497. parsedLine.CopyVerbatim(line);
  498. break;
  499. }
  500. if (parsedLine.IsComment())
  501. continue;
  502. if (!state.Process(parsedLine, output, this->LastResult))
  503. return false;
  504. }
  505. while (state.ReadLine(input, line));
  506. return state.Finished(this->LastResult);
  507. }
  508. //----------------------------------------------------------------------------
  509. bool cmVisualStudioSlnParser::ParseBOM(std::istream& input,
  510. std::string& line,
  511. State& state)
  512. {
  513. char bom[4];
  514. if (!input.get(bom, 4))
  515. {
  516. this->LastResult.SetError(ResultErrorReadingInput, 1);
  517. return false;
  518. }
  519. this->LastResult.HadBOM =
  520. (bom[0] == char(0xEF) && bom[1] == char(0xBB) && bom[2] == char(0xBF));
  521. if (!state.ReadLine(input, line))
  522. {
  523. this->LastResult.SetError(ResultErrorReadingInput, 1);
  524. return false;
  525. }
  526. if (!this->LastResult.HadBOM)
  527. line = bom + line; // it wasn't a BOM, prepend it to first line
  528. return true;
  529. }
  530. //----------------------------------------------------------------------------
  531. bool cmVisualStudioSlnParser::ParseMultiValueTag(const std::string& line,
  532. ParsedLine& parsedLine,
  533. State& state)
  534. {
  535. size_t idxEqualSign = line.find('=');
  536. const std::string& fullTag = line.substr(0, idxEqualSign);
  537. if (!this->ParseTag(fullTag, parsedLine, state))
  538. return false;
  539. if (idxEqualSign != line.npos)
  540. {
  541. size_t idxFieldStart = idxEqualSign + 1;
  542. if (idxFieldStart < line.size())
  543. {
  544. size_t idxParsing = idxFieldStart;
  545. bool inQuotes = false;
  546. for (;;)
  547. {
  548. idxParsing = line.find_first_of(",\"", idxParsing);
  549. bool fieldOver = false;
  550. if (idxParsing == line.npos)
  551. {
  552. fieldOver = true;
  553. if (inQuotes)
  554. {
  555. this->LastResult.SetError(ResultErrorInputStructure,
  556. state.GetCurrentLine());
  557. return false;
  558. }
  559. }
  560. else if (line[idxParsing] == ',' && !inQuotes)
  561. fieldOver = true;
  562. else if (line[idxParsing] == '"')
  563. inQuotes = !inQuotes;
  564. if (fieldOver)
  565. {
  566. if (!this->ParseValue(line.substr(idxFieldStart,
  567. idxParsing - idxFieldStart),
  568. parsedLine))
  569. return false;
  570. if (idxParsing == line.npos)
  571. break; //end of last field
  572. idxFieldStart = idxParsing + 1;
  573. }
  574. ++idxParsing;
  575. }
  576. }
  577. }
  578. return true;
  579. }
  580. //----------------------------------------------------------------------------
  581. bool cmVisualStudioSlnParser::ParseSingleValueTag(const std::string& line,
  582. ParsedLine& parsedLine,
  583. State& state)
  584. {
  585. size_t idxEqualSign = line.find('=');
  586. const std::string& fullTag = line.substr(0, idxEqualSign);
  587. if (!this->ParseTag(fullTag, parsedLine, state))
  588. return false;
  589. if (idxEqualSign != line.npos)
  590. {
  591. if (!this->ParseValue(line.substr(idxEqualSign + 1), parsedLine))
  592. return false;
  593. }
  594. return true;
  595. }
  596. //----------------------------------------------------------------------------
  597. bool cmVisualStudioSlnParser::ParseKeyValuePair(const std::string& line,
  598. ParsedLine& parsedLine,
  599. State& /*state*/)
  600. {
  601. size_t idxEqualSign = line.find('=');
  602. if (idxEqualSign == line.npos)
  603. {
  604. parsedLine.CopyVerbatim(line);
  605. return true;
  606. }
  607. const std::string& key = line.substr(0, idxEqualSign);
  608. parsedLine.SetTag(cmSystemTools::TrimWhitespace(key));
  609. const std::string& value = line.substr(idxEqualSign + 1);
  610. parsedLine.AddValue(cmSystemTools::TrimWhitespace(value));
  611. return true;
  612. }
  613. //----------------------------------------------------------------------------
  614. bool cmVisualStudioSlnParser::ParseTag(const std::string& fullTag,
  615. ParsedLine& parsedLine,
  616. State& state)
  617. {
  618. size_t idxLeftParen = fullTag.find('(');
  619. if (idxLeftParen == fullTag.npos)
  620. {
  621. parsedLine.SetTag(cmSystemTools::TrimWhitespace(fullTag));
  622. return true;
  623. }
  624. parsedLine.SetTag(
  625. cmSystemTools::TrimWhitespace(fullTag.substr(0, idxLeftParen)));
  626. size_t idxRightParen = fullTag.rfind(')');
  627. if (idxRightParen == fullTag.npos)
  628. {
  629. this->LastResult.SetError(ResultErrorInputStructure,
  630. state.GetCurrentLine());
  631. return false;
  632. }
  633. const std::string& arg = cmSystemTools::TrimWhitespace(
  634. fullTag.substr(idxLeftParen + 1, idxRightParen - idxLeftParen - 1));
  635. if (arg[0] == '"')
  636. {
  637. if (arg[arg.size() - 1] != '"')
  638. {
  639. this->LastResult.SetError(ResultErrorInputStructure,
  640. state.GetCurrentLine());
  641. return false;
  642. }
  643. parsedLine.SetQuotedArg(arg.substr(1, arg.size() - 2));
  644. }
  645. else
  646. parsedLine.SetArg(arg);
  647. return true;
  648. }
  649. //----------------------------------------------------------------------------
  650. bool cmVisualStudioSlnParser::ParseValue(const std::string& value,
  651. ParsedLine& parsedLine)
  652. {
  653. const std::string& trimmed = cmSystemTools::TrimWhitespace(value);
  654. if (trimmed.empty())
  655. parsedLine.AddValue(trimmed);
  656. else if (trimmed[0] == '"' && trimmed[trimmed.size() - 1] == '"')
  657. parsedLine.AddQuotedValue(trimmed.substr(1, trimmed.size() - 2));
  658. else
  659. parsedLine.AddValue(trimmed);
  660. return true;
  661. }