Line data Source code
1 : /*! 2 : * \file esys/repo/sharing/gitmodules_sharing.cpp 3 : * \brief Parse ``.gitmodules`` (ADR-0042) 4 : * 5 : * \cond 6 : * __legal_b__ 7 : * 8 : * Copyright (c) 2026 Michel Gillet 9 : * Distributed under the MIT License. 10 : * (See accompanying file LICENSE.txt or 11 : * copy at https://opensource.org/licenses/MIT) 12 : * 13 : * __legal_e__ 14 : * \endcond 15 : * 16 : */ 17 : 18 : #include "esys/repo/esysrepo_prec.h" 19 : #include "esys/repo/sharing/gitmodules.h" 20 : 21 : #include <fstream> 22 : #include <sstream> 23 : 24 : namespace esys::repo::sharing 25 : { 26 : namespace 27 : { 28 : 29 76 : std::string trim_copy(const std::string &s) 30 : { 31 76 : auto begin = s.find_first_not_of(" \t\r\n"); 32 76 : if (begin == std::string::npos) return ""; 33 72 : auto end = s.find_last_not_of(" \t\r\n"); 34 72 : return s.substr(begin, end - begin + 1); 35 : } 36 : 37 18 : std::string unquote(std::string s) 38 : { 39 18 : s = trim_copy(s); 40 18 : if (s.size() >= 2 && ((s.front() == '"' && s.back() == '"') || (s.front() == '\'' && s.back() == '\''))) 41 0 : return s.substr(1, s.size() - 2); 42 18 : return s; 43 : } 44 : 45 : } // namespace 46 : 47 9 : void GitmodulesEntry::set_name(const std::string &name) 48 : { 49 9 : m_name = name; 50 9 : } 51 : 52 8 : const std::string &GitmodulesEntry::get_name() const 53 : { 54 8 : return m_name; 55 : } 56 : 57 9 : void GitmodulesEntry::set_path(const std::string &path) 58 : { 59 9 : m_path = path; 60 9 : } 61 : 62 41 : const std::string &GitmodulesEntry::get_path() const 63 : { 64 41 : return m_path; 65 : } 66 : 67 9 : void GitmodulesEntry::set_url(const std::string &url) 68 : { 69 9 : m_url = url; 70 9 : } 71 : 72 22 : const std::string &GitmodulesEntry::get_url() const 73 : { 74 22 : return m_url; 75 : } 76 : 77 6 : Result GitmodulesFile::parse_text(const std::string &text) 78 : { 79 6 : m_entries.clear(); 80 6 : GitmodulesEntry current; 81 6 : bool in_submodule = false; 82 : 83 6 : std::istringstream in(text); 84 6 : std::string line; 85 37 : while (std::getline(in, line)) 86 : { 87 : // Strip comments 88 31 : auto hash = line.find('#'); 89 31 : auto semi = line.find(';'); 90 31 : auto cut = std::string::npos; 91 31 : if (hash != std::string::npos) cut = hash; 92 31 : if (semi != std::string::npos && (cut == std::string::npos || semi < cut)) cut = semi; 93 31 : if (cut != std::string::npos) line = line.substr(0, cut); 94 31 : line = trim_copy(line); 95 40 : if (line.empty()) continue; 96 : 97 27 : if (line.front() == '[' && line.back() == ']') 98 : { 99 9 : if (in_submodule && (!current.get_path().empty() || !current.get_url().empty())) 100 3 : m_entries.push_back(current); 101 9 : current = GitmodulesEntry{}; 102 9 : in_submodule = false; 103 : 104 9 : std::string section = trim_copy(line.substr(1, line.size() - 2)); 105 : // [submodule "name"] 106 9 : const std::string prefix = "submodule"; 107 18 : if (section.size() >= prefix.size() && 108 9 : section.compare(0, prefix.size(), prefix) == 0) 109 : { 110 9 : auto q1 = section.find('"'); 111 9 : auto q2 = section.rfind('"'); 112 9 : if (q1 != std::string::npos && q2 != std::string::npos && q2 > q1) 113 : { 114 9 : current.set_name(section.substr(q1 + 1, q2 - q1 - 1)); 115 9 : in_submodule = true; 116 : } 117 : } 118 9 : continue; 119 9 : } 120 : 121 18 : if (!in_submodule) continue; 122 : 123 18 : auto eq = line.find('='); 124 18 : if (eq == std::string::npos) continue; 125 18 : std::string key = trim_copy(line.substr(0, eq)); 126 18 : std::string value = unquote(line.substr(eq + 1)); 127 18 : if (key == "path") 128 9 : current.set_path(value); 129 9 : else if (key == "url") 130 9 : current.set_url(value); 131 18 : } 132 : 133 6 : if (in_submodule && (!current.get_path().empty() || !current.get_url().empty())) m_entries.push_back(current); 134 : 135 12 : return ESYSREPO_RESULT(ResultCode::OK); 136 6 : } 137 : 138 1 : Result GitmodulesFile::parse_file(const std::string &path) 139 : { 140 1 : std::ifstream ifs(path); 141 1 : if (!ifs) return ESYSREPO_RESULT(ResultCode::FILE_NOT_EXISTING, path); 142 1 : std::ostringstream oss; 143 1 : oss << ifs.rdbuf(); 144 1 : return parse_text(oss.str()); 145 1 : } 146 : 147 6 : const std::vector<GitmodulesEntry> &GitmodulesFile::get_entries() const 148 : { 149 6 : return m_entries; 150 : } 151 : 152 : } // namespace esys::repo::sharing