diff --git a/include/args.hpp b/include/args.hpp index 8f4be06..76ce4e7 100644 --- a/include/args.hpp +++ b/include/args.hpp @@ -29,6 +29,7 @@ enum { OP_SYNC, OP_REM, OP_QUERY, + OP_UPGRADE, OP_PACMAN, // when it's different from -S,R,Q we gonna use pacman }; diff --git a/include/config.hpp b/include/config.hpp index a04ba8f..a16c51a 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -22,17 +22,6 @@ using std::filesystem::path; // so we don't need to include util.hpp for getConfigValue() string expandVar(string& str); -enum types { - STR, - BOOL -}; - -struct strOrBool { - types valueType; - string stringValue = ""; - bool boolValue = false; -}; - struct _color_t { fmt::rgb red; fmt::rgb green; @@ -77,17 +66,12 @@ class Config { // alpm transaction flags int flags; - std::map overrides; - - Config(); + Config(string_view configFile, string_view themeFile, string_view configDir); ~Config(); - void init(string &configFile, string &themeFile, string_view configDir); void initVars(); void initColors(); - bool isInitialized(); - void loadConfigFile(string_view filename); void loadPacmanConfigFile(string filename); void loadThemeFile(string_view filename); @@ -95,18 +79,6 @@ class Config { // stupid c++ that wants template functions in header template T getConfigValue(const string& value, T fallback) { - auto overridePos = overrides.find(value); - - // user wants a bool (overridable), we found an override matching the name, and the override is a bool. - if constexpr (std::is_same()) - if (overridePos != overrides.end() && overrides[value].valueType == BOOL) - return overrides[value].boolValue; - - // user wants a str (overridable), we found an override matching the name, and the override is a str. - if constexpr (std::is_same()) - if (overridePos != overrides.end() && overrides[value].valueType == STR) - return overrides[value].stringValue; - std::optional ret = this->tbl.at_path(value).value(); if constexpr (toml::is_string) // if we want to get a value that's a string return ret ? expandVar(ret.value()) : expandVar(fallback); @@ -119,7 +91,6 @@ class Config { private: toml::table tbl, theme_tbl; - bool initialized = false; }; extern std::unique_ptr config; @@ -200,7 +171,4 @@ gray = "#5a5a5a" #index = "#ff11cc" )#"; -inline string configfile; -inline string themefile; - #endif diff --git a/include/util.hpp b/include/util.hpp index 0add143..77131fc 100644 --- a/include/util.hpp +++ b/include/util.hpp @@ -62,6 +62,7 @@ enum prompt_yn { PROMPT_YN_CONTINUE_WITHOUT_DIFF, PROMPT_YN_EDIT_PKGBUILD, PROMPT_YN_PROCEED_INSTALL, + PROMPT_YN_PROCEED_UPGRADE, PROMPT_YN_PROCEED_TRANSACTION, PROMPT_YN_CLEANBUILD, }; @@ -90,7 +91,7 @@ string expandVar(string& str); bool is_numerical(string_view s, bool allowSpace = false); bool taur_read_exec(vector cmd, string& output, bool exitOnFailure = true); void interruptHandler(int); -bool taur_exec(vector cmd, bool exitOnFailure = true); +bool taur_exec(vector cmd, bool exitOnFailure = true); void sanitizeStr(string& str); bool is_package_from_syncdb(const char *name, alpm_list_t *syncdbs); bool commitTransactionAndRelease(bool soft = false); @@ -255,6 +256,10 @@ bool askUserYorN(bool def, prompt_yn pr, Args&&... args) { log_printf(INFO, BOLD, _("Proceed with the installation? {}"), inputs_str); NOCONFIRM(YES); break; + case PROMPT_YN_PROCEED_UPGRADE: + log_printf(INFO, BOLD, _("Would you like to upgrade the above packages? {}"), inputs_str); + NOCONFIRM(YES); + break; case PROMPT_YN_PROCEED_TRANSACTION: log_printf(INFO, BOLD, _("Would you like to proceed with this transaction? {}"), inputs_str); NOCONFIRM(YES); diff --git a/src/args.cpp b/src/args.cpp index ddd6df4..a6af628 100644 --- a/src/args.cpp +++ b/src/args.cpp @@ -62,7 +62,9 @@ int parsearg_op(int opt, int dryrun) { op.op = (op.op != OP_MAIN ? 0 : OP_PACMAN); break; case 'U': if(dryrun) break; - op.op = (op.op != OP_MAIN ? 0 : OP_PACMAN); break; + op.op = (op.op != OP_MAIN ? 0 : OP_UPGRADE); + op.requires_root = true; + break; case 'V': if(dryrun) break; op.version = 1; break; @@ -88,34 +90,31 @@ int parsearg_op(int opt, int dryrun) { int parsearg_global(int opt) { switch (opt) { case OP_CACHEDIR: - config->overrides["general.cacheDir"] = {STR, strndup(optarg, PATH_MAX)}; + config->cacheDir = strndup(optarg, PATH_MAX); break; case OP_COLORS: fmt::disable_colors = !((bool)std::atoi(optarg)); - config->overrides["general.colors"] = {BOOL, "", (bool)std::atoi(optarg)}; + config->colors = (bool)std::atoi(optarg); break; case OP_DEBUG: - config->overrides["general.debug"] = {BOOL, "", true}; + config->debug = true; break; case OP_AURONLY: case 'a': - config->overrides["general.aurOnly"] = {BOOL, "", true}; + config->aurOnly = true; break; case OP_SUDO: - config->overrides["general.sudo"] = {STR, strndup(optarg, PATH_MAX)}; + config->sudo = strndup(optarg, PATH_MAX); break; case OP_NOCONFIRM: config->noconfirm = true; break; case OP_USEGIT: case 'g': - config->overrides["general.useGit"] = {BOOL, "", true}; + config->useGit = true; break; case OP_CONFIG: - configfile = strndup(optarg, PATH_MAX); - break; case OP_THEME: - themefile = strndup(optarg, PATH_MAX); break; default: return 1; diff --git a/src/config.cpp b/src/config.cpp index ae70089..ae0f8ab 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -8,8 +8,6 @@ using std::ofstream; using std::ifstream; -Config::Config() {} - Config::~Config() { if (this->handle) { alpm_trans_release(this->handle); @@ -21,10 +19,7 @@ Config::~Config() { } // initialize Config, can only be ran once for each Config instance. -void Config::init(string &configFile, string &themeFile, string_view configDir) { - if (this->initialized) - return; - +Config::Config(string_view configFile, string_view themeFile, string_view configDir) { bool newUser = false; if (!std::filesystem::exists(configDir)) { @@ -36,13 +31,13 @@ void Config::init(string &configFile, string &themeFile, string_view configDir) if (!std::filesystem::exists(configFile)) { log_println(WARN, _("{} not found, generating new one"), configFile); // https://github.com/hyprwm/Hyprland/blob/main/src/config/ConfigManager.cpp#L681 - ofstream f(configFile, std::ios::trunc); + ofstream f(configFile.data(), std::ios::trunc); f << AUTOCONFIG; f.close(); } if (!std::filesystem::exists(themeFile)) { log_println(WARN, _("{} not found, generating new one"), themeFile); - ofstream f(themeFile, std::ios::trunc); + ofstream f(themeFile.data(), std::ios::trunc); f << AUTOTHEME; f.close(); } @@ -50,10 +45,9 @@ void Config::init(string &configFile, string &themeFile, string_view configDir) this->loadConfigFile(configFile); this->loadThemeFile(themeFile); - this->initialized = true; - if (!std::filesystem::exists(config->cacheDir)) { - log_println(WARN, _("TabAUR cache folder was not found, Creating folders at {}!"), config->cacheDir.string()); - std::filesystem::create_directories(config->cacheDir); + if (!std::filesystem::exists(this->cacheDir)) { + log_println(WARN, _("TabAUR cache folder was not found, Creating folders at {}!"), this->cacheDir.string()); + std::filesystem::create_directories(this->cacheDir); } if (newUser) @@ -66,11 +60,6 @@ void Config::init(string &configFile, string &themeFile, string_view configDir) "Thank you!\n")); } -// get initialized variable -bool Config::isInitialized() { - return this->initialized; -} - /* * initialize all the "config.toml" variables * and sanitize them (never trust user's input) diff --git a/src/main.cpp b/src/main.cpp index c02f69b..1569260 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,6 +17,8 @@ * along with this program. If not, see . */ +#include +#include #pragma GCC diagnostic ignored "-Wvla" #include "args.hpp" @@ -137,14 +139,19 @@ void execPacman(int argc, char *argv[]) { if (execvp(args[0], const_cast(args.data())) == -1) perror("execvp"); + exit(1); } int installPkg(alpm_list_t *pkgNames) { + if (!pkgNames) + return false; + bool useGit = config->useGit; path cacheDir = config->cacheDir; - bool returnStatus = true, stat; + bool returnStatus = true; + bool stat; vector pacmanPkgs; // list of pacman packages to install, to avoid spamming pacman. vector pkgNamesVec; @@ -182,73 +189,101 @@ int installPkg(alpm_list_t *pkgNames) { if (!update_aur_cache()) log_println(ERROR, _("Failed to get information about {}"), (config->cacheDir / "packages.aur").string()); - // this feels horrible despite being 100% correct and probably not problematic. - // it just feels like we should ask for every package. + // I swear there was a comment here.. vector AURPkgs = filterAURPkgsNames(pkgNamesVec, alpm_get_syncdbs(config->handle), true); + for (const auto& pkg : pkgNamesVec) + { + if (std::find(AURPkgs.begin(), AURPkgs.end(), pkg) == AURPkgs.end()) + pacmanPkgs.push_back(pkg.data()); + } + if (!op.op_s_cleanbuild && !AURPkgs.empty()) pkgsToCleanBuild = askUserForList(AURPkgs, PROMPT_LIST_CLEANBUILDS); if (!config->noconfirm && !AURPkgs.empty()) pkgsToReview = askUserForList(AURPkgs, PROMPT_LIST_REVIEWS); - - for (size_t i = 0; i < pkgNamesVec.size(); i++) { - vector pkgs = backend->search(pkgNamesVec[i], useGit, config->aurOnly, true); + for (auto& pkg_name : AURPkgs) { - optional> oSelectedPkg = askUserForPkg(pkgs, *backend, useGit); + path pkgDir = path(cacheDir) / pkg_name; - if (!oSelectedPkg) { + stat = useGit ? backend->download_git(AUR_URL_GIT(pkg_name), pkgDir) : backend->download_tar(AUR_URL_TAR(pkg_name), pkgDir); + if (!stat) { + log_println(ERROR, _("Failed to download {}"), pkg_name); returnStatus = false; continue; } - vector selectedPkg = oSelectedPkg.value(); + } - for (size_t i = 0; i < selectedPkg.size(); i++) { - TaurPkg_t pkg = selectedPkg[i]; + for (string_view& pkg : pkgsToCleanBuild) { + path pkgDir = path(cacheDir) / pkg; + if (!useGit) { + log_println(INFO, _("Removing {}"), pkgDir.c_str()); + std::filesystem::remove_all(pkgDir); + } + else { + log_println(INFO, _("Cleaning {}"), pkgDir.c_str()); + taur_exec({config->git, "-C", pkgDir.c_str(), "clean", "-xffd"}); + } + } - if (pkg.db_name != "aur") { - pacmanPkgs.push_back(pkg.name); - continue; - } + // cmd is just a workaround for making it possible + // to pass flags to the editor, e.g nano --modernbindings + // instead of creating another config variable + for (string_view& pkg : pkgsToReview) { + path pkgDir = path(cacheDir) / pkg; + + vector cmd; + for (auto& str : config->editor) + cmd.push_back(str); + cmd.push_back((pkgDir / "PKGBUILD").string()); + + taur_exec(cmd); + } - path pkgDir = path(cacheDir) / pkg.name; - bool cleanBuild = op.op_s_cleanbuild ? false : std::find(pkgsToCleanBuild.begin(), pkgsToCleanBuild.end(), pkg.name) != pkgsToCleanBuild.end(); - bool review = config->noconfirm ? false : std::find(pkgsToReview.begin(), pkgsToReview.end(), pkg.name) != pkgsToReview.end(); + if (!pkgsToReview.empty() && !askUserYorN(YES, PROMPT_YN_PROCEED_INSTALL)) + return false; - if (cleanBuild) { - log_println(INFO, _("Removing {}"), pkgDir.c_str()); - std::filesystem::remove_all(pkgDir); - } + if (!config->aurOnly && (op.op_s_upgrade || !pacmanPkgs.empty())) { + if (op.op_s_upgrade) + log_println(INFO, _("Upgrading system packages!")); + else + log_println(INFO, _("Installing system packages!")); - stat = useGit ? backend->download_git(AUR_URL_GIT(pkg.name), pkgDir) : backend->download_tar(AUR_URL_TAR(pkg.name), pkgDir); - if (!stat) { - log_println(ERROR, _("Failed to download {}"), pkg.name); - returnStatus = false; - continue; - } + string op_s = "-S"; - if (review || askUserYorN(YES, PROMPT_YN_EDIT_PKGBUILD, pkg.name)) { - // cmd is just a workaround for making possible - // that editor can have flags, e.g nano --modernbindings - // instead of creating another config variable - // This is really ugly - // because u can't convert std::vector to std::vector - vector _cmd; - vector cmd; - for (auto& str : config->editor) - _cmd.push_back(str); - _cmd.push_back((pkgDir / "PKGBUILD").string()); - - for (auto& str : _cmd) - cmd.push_back(str.c_str()); - - taur_exec(cmd); - - if (!askUserYorN(YES, PROMPT_YN_PROCEED_INSTALL)) - return false; - } + if (op.op_s_sync) + op_s += 'y'; + + if (op.op_s_upgrade) + op_s += 'u'; + + pacman_exec(op_s, pacmanPkgs); + } + + if (op.op_s_upgrade) { + log_println(INFO, _("Upgrading AUR packages!")); + backend->update_all_aur_pkgs(cacheDir, useGit); + + pkgs_to_install = ""; // Reset the list. + } + + for (size_t i = 0; i < AURPkgs.size(); i++) { + vector pkgs = backend->search(AURPkgs[i], useGit, config->aurOnly, true); + + optional> oSelectedPkgs = askUserForPkg(pkgs, *backend, useGit); + + if (!oSelectedPkgs) { + returnStatus = false; + continue; + } + + vector selectedPkgs = oSelectedPkgs.value(); + + for (TaurPkg_t &pkg : selectedPkgs) { + path pkgDir = path(cacheDir) / pkg.name; stat = backend->handle_aur_depends(pkg, cacheDir, backend->get_all_local_pkgs(true), useGit); @@ -261,8 +296,10 @@ int installPkg(alpm_list_t *pkgNames) { stat = backend->build_pkg(pkg.name, pkgDir, false); if (!stat) { - log_println(ERROR, _("Building your package has failed.")); + log_println(ERROR, _("Building {} has failed."), pkg.name); returnStatus = false; + pkgs_failed_to_build += pkg.name + ' '; + log_println(DEBUG, "pkgs_failed_to_build = {}", pkgs_failed_to_build); continue; } @@ -279,28 +316,11 @@ int installPkg(alpm_list_t *pkgNames) { } } - if (!config->aurOnly && (op.op_s_upgrade || !pacmanPkgs.empty())) { - if (op.op_s_upgrade) - log_println(INFO, _("Upgrading system packages!")); - else - log_println(INFO, _("Installing system packages!")); - - string op_s = "-S"; - - if (op.op_s_sync) - op_s += 'y'; - - if (op.op_s_upgrade) - op_s += 'u'; - - pacman_exec(op_s, pacmanPkgs); + if (!pkgs_failed_to_build.empty()) { + pkgs_failed_to_build.erase(pkgs_failed_to_build.end() - 1); + log_println(WARN, fg(color.red), _("Failed to upgrade: {}"), pkgs_failed_to_build); + log_println(INFO, fg(color.cyan), _("Tip: try to run taur with \"-S {}\" (e.g \"taur -S {}\")"), pkgs_failed_to_build, pkgs_failed_to_build); } - - if (op.op_s_upgrade) { - log_println(INFO, _("Upgrading AUR packages!")); - backend->update_all_aur_pkgs(cacheDir, useGit); - } - return returnStatus; } @@ -357,7 +377,7 @@ bool removePkg(alpm_list_t *pkgNames) { try { auto pkg = std::find(pkgs.begin(), pkgs.end(), includedPkgs[i]); - if (pkgs.begin() >= pkg || pkg >= pkgs.end()) + if (pkg >= pkgs.end()) continue; size_t pkgIndex = std::distance(pkgs.begin(), pkg); @@ -518,6 +538,35 @@ bool queryPkgs(alpm_list_t *pkgNames) { return true; } +bool upgradePkgs(alpm_list_t *pkgNames) { + if (!pkgNames) { + return false; + } + + if (alpm_trans_init(config->handle, config->flags)) { + log_println(ERROR, _("Failed to initialize transaction ({})"), alpm_strerror(alpm_errno(config->handle))); + return false; + } + + for (; pkgNames; pkgNames = pkgNames->next) { + alpm_pkg_t *pkg; + alpm_pkg_load(config->handle, (const char *)pkgNames->data, 1, alpm_option_get_local_file_siglevel(config->handle), &pkg); + + if (!pkg) { + log_println(ERROR, _("Failed to load package {}! ({})"), (const char *)(pkgNames->data), alpm_strerror(alpm_errno(config->handle))); + return false; // Yes, I am ignoring the transaction we just created. + } + + if (alpm_add_pkg(config->handle, pkg)) { + log_println(ERROR, _("Failed to add package {} to transaction! ({})"), (const char *)(pkgNames->data), alpm_strerror(alpm_errno(config->handle))); + return false; + } + + } + + return commitTransactionAndRelease(true); +} + /** Sets up gettext localization. Safe to call multiple times. */ /* Inspired by the monotone function localize_monotone. */ @@ -534,6 +583,46 @@ static void localize(void) { } #endif +// parseargs() but only for parsing the user config path trough args +// and so we can directly construct Config +static bool parse_config_path(int argc, char* argv[], string& configFile, string& themeFile) { + int opt = 0; + int option_index = 0; + opterr = 0; + const char *optstring = "C:"; + static const struct option opts[] = + { + {"config", required_argument, 0, OP_CONFIG}, + {"theme", required_argument, 0, OP_THEME}, + {0,0,0,0} + }; + + while ((opt = getopt_long(argc, argv, optstring, opts, &option_index)) != -1) { + if (opt == 0 || opt == '?') + continue; + + switch (opt) { + case OP_CONFIG: + configFile = strndup(optarg, PATH_MAX); + if (!std::filesystem::exists(configFile)) + die("config file '{}' doesn't exist", configFile); + + break; + case OP_THEME: + themeFile = strndup(optarg, PATH_MAX); + if (!std::filesystem::exists(themeFile)) + die("theme file '{}' doesn't exist", themeFile); + + break; + + default: + return false; + } + } + + return true; +} + // function taken from pacman int parseargs(int argc, char* argv[]) { // default @@ -542,6 +631,8 @@ int parseargs(int argc, char* argv[]) { int opt = 0; int option_index = 0; int result = 0; + opterr = 1; // re-enable since before we disabled for "invalid option" error + optind = 0; const char *optstring = "DFQRSTUVaihqsurytns"; static const struct option opts[] = { @@ -664,21 +755,21 @@ int parseargs(int argc, char* argv[]) { // main int main(int argc, char *argv[]) { - config = std::make_unique(); string configDir = getConfigDir(); #if defined(ENABLE_NLS) localize(); #endif - configfile = (configDir + "/config.toml"); - themefile = (configDir + "/theme.toml"); + string configfile = (configDir + "/config.toml"); + string themefile = (configDir + "/theme.toml"); + parse_config_path(argc, argv, configfile, themefile); + + config = std::make_unique(configfile, themefile, configDir); if (parseargs(argc, argv)) return 1; - config->init(configfile, themefile, configDir); - if (op.test_colors) { test_colors(); return 0; @@ -716,7 +807,9 @@ int main(int argc, char *argv[]) { log_println(WARN, _("Watch out when using the -R operation in TabAUR, it has been tested pretty well, but you should always watch out for any errors, Please use pacman or be careful")); return removePkg(taur_targets.get()) ? 0 : 1; case OP_QUERY: - return (queryPkgs(taur_targets.get())) ? 0 : 1; + return queryPkgs(taur_targets.get()) ? 0 : 1; + case OP_UPGRADE: + return upgradePkgs(taur_targets.get()) ? 0 : 1; case OP_SYSUPGRADE: return (updateAll()) ? 0 : 1; default: diff --git a/src/taur.cpp b/src/taur.cpp index a88a508..9ea3afd 100644 --- a/src/taur.cpp +++ b/src/taur.cpp @@ -3,17 +3,19 @@ #include "taur.hpp" #include "config.hpp" #include "util.hpp" +#include #include +#include TaurBackend::TaurBackend(Config& cfg) : config(cfg) {} bool TaurBackend::download_git(string_view url, path out_path) { if (std::filesystem::exists(path(out_path) / ".git")) { - return taur_exec({config.git.c_str(), "-C", out_path.c_str(), "pull", "--rebase", "--autostash", "--ff-only"}); + return taur_exec({config.git.c_str(), "-C", out_path, "pull", "--autostash", "--rebase", "--ff-only", "--force"}); } else { if (std::filesystem::exists(path(out_path))) std::filesystem::remove_all(out_path); - return taur_exec({config.git.c_str(), "clone", url.data(), out_path.c_str()}); + return taur_exec({config.git.c_str(), "clone", url.data(), out_path}); } } @@ -238,7 +240,7 @@ bool TaurBackend::build_pkg(string_view pkg_name, string extracted_path, bool al if (!alreadyprepared) { log_println(INFO, _("Verifying package sources..")); - makepkg_exec({"--verifysource", "--skippgpcheck", "-f", "-Cc"}); + makepkg_exec({"--verifysource", "--skippgpcheck", "-fs", "-Cc"}); log_println(INFO, _("Preparing for compilation..")); makepkg_exec({"--nobuild", "--skippgpcheck", "-fs", "-C", "--ignorearch"}); @@ -251,7 +253,7 @@ bool TaurBackend::build_pkg(string_view pkg_name, string extracted_path, bool al /*log_println(INFO, _("Compiling {} in 3 seconds, you can cancel at this point if you can't compile."), pkg_name); sleep(3);*/ - return makepkg_exec({"-f", "--noconfirm", "--noextract", "--noprepare", "--nocheck", "--holdver", "--ignorearch", "-c"}, false); + return makepkg_exec({"-fs", "--noconfirm", "--noextract", "--noprepare", "--nocheck", "--holdver", "--ignorearch", "-c"}, false); } else log_println(INFO, _("{} exists already, skipping..."), built_pkg); @@ -375,68 +377,99 @@ bool TaurBackend::handle_aur_depends(TaurPkg_t pkg, path out_path, vector pkgs = this->get_all_local_pkgs(true); + vector localPkgs = this->get_all_local_pkgs(true); - if (pkgs.empty()) { + if (localPkgs.empty()) { log_println(INFO, _("No AUR packages found in your system.")); return true; } vector pkgNames; - pkgNames.reserve(pkgs.size()); + pkgNames.reserve(localPkgs.size()); - for (size_t i = 0; i < pkgs.size(); ++i) - pkgNames.push_back(pkgs[i].name); + for (const TaurPkg_t &pkg : localPkgs) + pkgNames.push_back(pkg.name); vector onlinePkgs = this->fetch_pkgs(pkgNames, useGit); int updatedPkgs = 0; int attemptedDownloads = 0; - if (onlinePkgs.size() != pkgs.size()) - log_println(WARN, _("Couldn't get all packages! (searched {} packages, got {}) Still trying to update the others."), pkgs.size(), onlinePkgs.size()); + if (onlinePkgs.size() != localPkgs.size()) + log_println(WARN, _("Couldn't get all packages! (searched {} packages, got {}) Still trying to update the others."), localPkgs.size(), onlinePkgs.size()); - for (size_t i = 0; i < onlinePkgs.size(); i++) { - size_t pkgIndex; - bool found = false; - bool alrprepared = false; - - for (pkgIndex = 0; pkgIndex < pkgs.size(); pkgIndex++) { - if (pkgs[pkgIndex].name == onlinePkgs[i].name) { - found = true; - break; - } - } + log_println(INFO, "Here's a list of packages that may be updated:"); - log_println(DEBUG, "onlinePkgs.name = {}", onlinePkgs[i].name); - log_println(DEBUG, "onlinePkgs.totaldepends = {}", onlinePkgs[i].totaldepends); + vector> potentialUpgradeTargets; - if (!found) { - log_println(WARN, _("We couldn't find {} in the local pkg database, This shouldn't happen."), onlinePkgs[i].name); + for (size_t i = 0; i < onlinePkgs.size(); i++) { + TaurPkg_t &pkg = onlinePkgs[i]; + auto pkgIteratorInLocalPkgs = std::find_if(localPkgs.begin(), localPkgs.end(), [&pkg] (const TaurPkg_t &element) { return element.name == pkg.name; }); + size_t pkgIndexInLocalPkgs = std::distance(localPkgs.begin(), pkgIteratorInLocalPkgs); + TaurPkg_t &localPkg = localPkgs[pkgIndexInLocalPkgs]; + + if (hasEnding(pkg.name, "-git")) { + potentialUpgradeTargets.push_back(std::make_tuple(pkg, localPkg)); + log_println(INFO, "- {} (from {} to {}, (dev package, may change despite AUR version))", localPkg.name, localPkg.version, pkg.version); continue; } - - if (pkgs[pkgIndex].version == onlinePkgs[i].version) { - log_println(DEBUG, "pkg {} has no update, local: {}, online: {}, skipping!", pkgs[pkgIndex].name, pkgs[pkgIndex].version, onlinePkgs[i].version); + + if ((localPkg.version != pkg.version) && alpm_pkg_vercmp(pkg.version.c_str(), localPkg.version.c_str()) == 1) { + potentialUpgradeTargets.push_back(std::make_tuple(pkg, localPkg)); + log_println(INFO, "- {} (from {} to {})", localPkg.name, localPkg.version, pkg.version); continue; } + } + + if (!askUserYorN(true, PROMPT_YN_PROCEED_UPGRADE)) + return false; + + for (const auto &potentialUpgrade : potentialUpgradeTargets) { + // size_t pkgIndex; + // bool found = false; + bool alrprepared = false; + + // for (pkgIndex = 0; pkgIndex < localPkgs.size(); pkgIndex++) { + // if (localPkgs[pkgIndex].name == onlinePkgs[i].name) { + // found = true; + // break; + // } + // } + + TaurPkg_t potentialUpgradeTargetTo = std::get<0>(potentialUpgrade); + TaurPkg_t potentialUpgradeTargetFrom = std::get<1>(potentialUpgrade); - path pkgDir = cacheDir / onlinePkgs[i].name; + log_println(DEBUG, "potentialUpgradeTarget.name = {}", potentialUpgradeTargetTo.name); + log_println(DEBUG, "potentialUpgradeTarget.totaldepends = {}", potentialUpgradeTargetTo.totaldepends); - log_println(INFO, _("Downloading {}."), onlinePkgs[i].name); + // if (!found) { + // log_println(WARN, _("We couldn't find {} in the local pkg database, This shouldn't happen."), onlinePkgs[i].name); + // continue; + // } + + bool isGitPackage = hasEnding(potentialUpgradeTargetTo.name, "-git"); + + // if (!isGitPackage && localPkgs[pkgIndex].version == onlinePkgs[i].version) { + // log_println(DEBUG, "pkg {} has no update, local: {}, online: {}, skipping!", localPkgs[pkgIndex].name, localPkgs[pkgIndex].version, onlinePkgs[i].version); + // continue; + // } + + path pkgDir = cacheDir / potentialUpgradeTargetTo.name; + + log_println(INFO, _("Downloading {}."), potentialUpgradeTargetTo.name); if (!useGit) std::filesystem::remove_all(pkgDir); - bool downloadSuccess = this->download_pkg(onlinePkgs[i].aur_url, pkgDir); + bool downloadSuccess = this->download_pkg(potentialUpgradeTargetTo.aur_url, pkgDir); if (!downloadSuccess) { - log_println(WARN, _("Failed to download package {}!"), onlinePkgs[i].name); + log_println(WARN, _("Failed to download package {}!"), potentialUpgradeTargetTo.name); continue; } // workaround for -git package because they are "special" - if (hasEnding(pkgs[pkgIndex].name, "-git")) { + if (isGitPackage) { alrprepared = true; std::filesystem::current_path(pkgDir); makepkg_exec({"--verifysource", "-fA"}); @@ -446,7 +479,7 @@ bool TaurBackend::update_all_aur_pkgs(path cacheDir, bool useGit) { string versionInfo = shell_exec("grep 'pkgver=' " + pkgDir.string() + "/PKGBUILD | cut -d= -f2"); if (versionInfo.empty()) { - log_println(WARN, _("Failed to parse version information from {}'s PKGBUILD, You might be able to ignore this safely."), pkgs[pkgIndex].name); + log_println(WARN, _("Failed to parse version information from {}'s PKGBUILD, You might be able to ignore this safely."), potentialUpgradeTargetTo.name); continue; } @@ -459,20 +492,20 @@ bool TaurBackend::update_all_aur_pkgs(path cacheDir, bool useGit) { if (!epoch.empty() && epoch[0] != '\0') versionInfo = epoch + ':' + versionInfo; - log_println(DEBUG, "pkg {} versions: local {} vs online {}", pkgs[pkgIndex].name, pkgs[pkgIndex].version, onlinePkgs[i].version); + log_println(DEBUG, "pkg {} versions: local {} vs online {}", potentialUpgradeTargetTo.name, potentialUpgradeTargetTo.version, potentialUpgradeTargetFrom.version); - if ((alpm_pkg_vercmp(pkgs[pkgIndex].version.data(), versionInfo.c_str())) == 0) { + if ((alpm_pkg_vercmp(potentialUpgradeTargetFrom.version.data(), versionInfo.c_str())) == 0) { - log_println(DEBUG, _("pkg {} has the same version on the AUR than in its PKGBUILD, local: {}, online: {}, PKGBUILD: {}, skipping!"), pkgs[pkgIndex].name, - pkgs[pkgIndex].version, onlinePkgs[i].version, versionInfo); + log_println(DEBUG, _("pkg {} has the same version on the AUR than in its PKGBUILD, local: {}, online: {}, PKGBUILD: {}, skipping!"), potentialUpgradeTargetFrom.name, + potentialUpgradeTargetFrom.version, potentialUpgradeTargetTo.version, versionInfo); continue; } - log_println(INFO, _("Upgrading package {} from version {} to version {}!"), pkgs[pkgIndex].name, pkgs[pkgIndex].version, onlinePkgs[i].version); + log_println(INFO, _("Upgrading package {} from version {} to version {}!"), potentialUpgradeTargetFrom.name, potentialUpgradeTargetTo.version, potentialUpgradeTargetTo.version); attemptedDownloads++; - this->handle_aur_depends(onlinePkgs[i], pkgDir, this->get_all_local_pkgs(true), useGit); - bool installSuccess = this->build_pkg(pkgs[pkgIndex].name, pkgDir, alrprepared); + this->handle_aur_depends(potentialUpgradeTargetTo, pkgDir, this->get_all_local_pkgs(true), useGit); + bool installSuccess = this->build_pkg(potentialUpgradeTargetTo.name, pkgDir, alrprepared); if (installSuccess) { pkgs_to_install += built_pkg + ' '; @@ -480,21 +513,18 @@ bool TaurBackend::update_all_aur_pkgs(path cacheDir, bool useGit) { updatedPkgs++; // prevent duplicated entries - pkgs.erase(pkgs.begin() + pkgIndex); + //localPkgs.erase(localPkgs.begin() + pkgIndex); } else { - pkgs_failed_to_build += pkgs[pkgIndex].name + ' '; + pkgs_failed_to_build += potentialUpgradeTargetTo.name + ' '; log_println(DEBUG, "pkgs_failed_to_build = {}", pkgs_failed_to_build); } } if (pkgs_to_install.size() <= 0) { log_println(WARN, _("No packages to be upgraded.")); - // oh no, a goto oh noooo this program is ruined - goto text; + return false; } - // remove the last ' ' so we can pass it to pacman - pkgs_to_install.erase(pkgs_to_install.end() - 1); if (!pacman_exec("-U", split(pkgs_to_install, ' '), false)) { log_println(ERROR, _("Failed to install/upgrade packages")); return false; @@ -502,7 +532,6 @@ bool TaurBackend::update_all_aur_pkgs(path cacheDir, bool useGit) { log_println(INFO, _("Upgraded {}/{} packages."), updatedPkgs, attemptedDownloads); -text: if (attemptedDownloads > updatedPkgs) { pkgs_failed_to_build.erase(pkgs_failed_to_build.end() - 1); log_println(WARN, fg(color.red), _("Failed to upgrade: {}"), pkgs_failed_to_build); @@ -615,11 +644,11 @@ vector TaurBackend::search(string_view query, bool useGit, bool aurOn if (!aurOnly) pacPkgs = this->search_pac(query); - size_t count = aurPkgs.size() + pacPkgs.size(); + size_t allPkgsSize = aurPkgs.size() + pacPkgs.size(); vector combined; - combined.reserve(count); + combined.reserve(allPkgsSize); if (!aurPkgs.empty()) combined.insert(combined.end(), aurPkgs.begin(), aurPkgs.end()); diff --git a/src/util.cpp b/src/util.cpp index 63c04cf..520c1f8 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -425,11 +425,15 @@ bool taur_read_exec(vector cmd, string& output, bool exitOnFailure } /** Executes commands with execvp() and keep the program running without existing - * @param cmd The command to execute + * @param cmd_str The command to execute * @param exitOnFailure Whether to call exit(1) on command failure. * @return true if the command successed, else false */ -bool taur_exec(vector cmd, bool exitOnFailure) { +bool taur_exec(vector cmd_str, bool exitOnFailure) { + + vector cmd; + for (string& str : cmd_str) + cmd.push_back(str.c_str()); int pid = fork(); @@ -466,21 +470,21 @@ bool taur_exec(vector cmd, bool exitOnFailure) { * @return true if the command successed, else false */ bool makepkg_exec(vector const& args, bool exitOnFailure) { - vector ccmd = {config->makepkgBin.c_str()}; + vector cmd = {config->makepkgBin}; if (config->noconfirm) - ccmd.push_back("--noconfirm"); + cmd.push_back("--noconfirm"); if (!config->colors) - ccmd.push_back("--nocolor"); + cmd.push_back("--nocolor"); - ccmd.push_back("--config"); - ccmd.push_back(config->makepkgConf.c_str()); + cmd.push_back("--config"); + cmd.push_back(config->makepkgConf); for (auto& str : args) - ccmd.push_back(str.c_str()); + cmd.push_back(str.c_str()); - return taur_exec(ccmd, exitOnFailure); + return taur_exec(cmd, exitOnFailure); } /** Convinient way to executes pacman commands with taur_exec() and keep the program running without existing @@ -492,29 +496,29 @@ bool makepkg_exec(vector const& args, bool exitOnFailure) { * @return true if the command successed, else false */ bool pacman_exec(string_view op, vector const& args, bool exitOnFailure, bool root) { - vector ccmd; + vector cmd; if (root) - ccmd = {config->sudo.c_str(), "pacman", op.data()}; + cmd = {config->sudo, "pacman", op.data()}; else - ccmd = {"pacman", op.data()}; + cmd = {"pacman", op.data()}; if (config->noconfirm) - ccmd.push_back("--noconfirm"); + cmd.push_back("--noconfirm"); if (config->colors) - ccmd.push_back("--color=auto"); + cmd.push_back("--color=auto"); else - ccmd.push_back("--color=never"); + cmd.push_back("--color=never"); - ccmd.push_back("--config"); - ccmd.push_back(config->pmConfig.c_str()); - ccmd.push_back("--"); + cmd.push_back("--config"); + cmd.push_back(config->pmConfig); + cmd.push_back("--"); for (auto& str : args) - ccmd.push_back(str.c_str()); + cmd.push_back(str); - return taur_exec(ccmd, exitOnFailure); + return taur_exec(cmd, exitOnFailure); } /** Free a list and every single item in it.