// Copyright (C) 2024 Simon Quigley // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "update-maintainer.h" #include #include #include #include #include #include #include namespace fs = std::filesystem; static const char* PREVIOUS_UBUNTU_MAINTAINERS[] = { "ubuntu core developers ", "ubuntu core developers ", "ubuntu motu developers " }; static const char* UBUNTU_MAINTAINER = "Ubuntu Developers "; class MaintainerUpdateException : public std::runtime_error { public: using std::runtime_error::runtime_error; }; static std::optional find_control_file(const fs::path &debian_dir) { fs::path control_in = debian_dir / "control.in"; fs::path control = debian_dir / "control"; if (fs::exists(control_in)) return control_in; if (fs::exists(control)) return control; return std::nullopt; } static fs::path find_changelog_file(const fs::path &debian_dir) { fs::path changelog = debian_dir / "changelog"; if (!fs::exists(changelog)) { throw MaintainerUpdateException("No changelog file found"); } return changelog; } static bool xsbc_managed_by_rules(const fs::path &debian_dir) { fs::path rules = debian_dir / "rules"; if (!fs::exists(rules)) return false; std::ifstream rf(rules); std::string line; while (std::getline(rf, line)) { if (line.find("XSBC-Original-") != std::string::npos) { return true; } } return false; } static std::string get_distribution(const fs::path &changelog_file) { // parse first line of changelog: "package (version) dist; urgency=..." // dist is the token after ')' std::ifstream f(changelog_file); if(!f) throw MaintainerUpdateException("Unable to open changelog."); std::string first_line; std::getline(f, first_line); size_t pos = first_line.find(')'); if(pos == std::string::npos) throw MaintainerUpdateException("Invalid changelog format"); pos++; while(pos < first_line.size() && std::isspace((unsigned char)first_line[pos])) pos++; size_t start = pos; while(pos < first_line.size() && !std::isspace((unsigned char)first_line[pos]) && first_line[pos] != ';') pos++; std::string dist = first_line.substr(start, pos - start); size_t dashpos = dist.find('-'); if (dashpos != std::string::npos) { dist = dist.substr(0, dashpos); } return dist; } static std::string read_file(const fs::path &p) { std::ifstream f(p); if(!f) throw MaintainerUpdateException("Cannot read file: " + p.string()); std::stringstream ss; ss << f.rdbuf(); return ss.str(); } static void write_file(const fs::path &p, const std::string &content) { std::ofstream f(p); if(!f) throw MaintainerUpdateException("Cannot write file: " + p.string()); f << content; } static std::optional get_field(const std::string &content, const std::string &field_regex) { std::regex r(field_regex, std::regex_constants::multiline); std::smatch m; if(std::regex_search(content, m, r)) { return m[1].str(); } return std::nullopt; } static std::string set_field(const std::string &content, const std::string &field_regex, const std::string &new_line) { std::regex r(field_regex, std::regex_constants::multiline); return std::regex_replace(content, r, new_line); } static void update_maintainer_file(const fs::path &control_file, const std::string &distribution, bool verbose) { std::string c = read_file(control_file); auto original_maintainer = get_field(c, "^Maintainer:\\s?(.*)$"); if(!original_maintainer) { throw MaintainerUpdateException("No Maintainer field found"); } std::string om = *original_maintainer; std::string om_lower = om; for (auto &ch : om_lower) ch = (char)std::tolower((unsigned char)ch); // Check previous ubuntu maintainers for (auto &pm : PREVIOUS_UBUNTU_MAINTAINERS) { std::string pm_lower = pm; for (auto &ch: pm_lower) ch=(char)std::tolower((unsigned char)ch); if(pm_lower == om_lower) { if(verbose) { std::cout<<"The old maintainer was: "<", lower_om.size()-11) != std::string::npos) { if(verbose) { std::cout<<"The Maintainer email is ubuntu.com address. Doing nothing.\n"; } return; } } // Debian distributions: stable, testing, unstable, experimental if(distribution=="stable"||distribution=="testing"||distribution=="unstable"||distribution=="experimental") { if(verbose) { std::cout<<"The package targets Debian. Doing nothing.\n"; } return; } // set XSBC-Original-Maintainer if needed auto orig_field = get_field(c, "^(?:[XSBC]*-)?Original-Maintainer:\\s?(.*)$"); if(orig_field && verbose) { std::cout<<"Overwriting original maintainer: "<< *orig_field <<"\n"; } if(verbose) { std::cout<<"The original maintainer is: "<< om <<"\n"; std::cout<<"Resetting as: "< [--verbose]" << std::endl; return 1; } std::string debian_directory = argv[1]; bool verbose = false; if(argc >=3 ) { std::string flag = argv[2]; if(flag == "--verbose") { verbose = true; } } try { update_maintainer(debian_directory, verbose); if(verbose) { std::cout << "Maintainer updated successfully." << std::endl; } } catch(const MaintainerUpdateException& e) { std::cerr << "Error: " << e.what() << std::endl; return 1; } return 0; }