#include <apt-pkg/cacheset.h>
#include <apt-pkg/cmndline.h>
#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
#include <apt-pkg/init.h>
-#include <apt-pkg/strutl.h>
#include <apt-pkg/pkgsystem.h>
-#include <apt-pkg/fileutl.h>
+#include <apt-pkg/strutl.h>
+#include <apt-pkg/statechanges.h>
+#include <apt-pkg/cacheiterators.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/depcache.h>
+#include <apt-pkg/macros.h>
+#include <apt-pkg/pkgcache.h>
+
+#include <apt-private/private-cmndline.h>
+#include <apt-private/private-output.h>
+#include <apt-private/private-main.h>
-#include <algorithm>
#include <errno.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
#include <fcntl.h>
-
-#include <apt-private/private-cmndline.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
#include <apti18n.h>
/*}}}*/
using namespace std;
-ostream c0out(0);
-ostream c1out(0);
-ostream c2out(0);
-ofstream devnull("/dev/null");
/* DoAuto - mark packages as automatically/manually installed {{{*/
static bool DoAuto(CommandLine &CmdL)
{
return true;
}
/*}}}*/
-/* DoHold - mark packages as hold by dpkg {{{*/
-static bool DoHold(CommandLine &CmdL)
+// DoSelection - wrapping around dpkg selections /*{{{*/
+static bool DoSelection(CommandLine &CmdL)
{
pkgCacheFile CacheFile;
pkgCache *Cache = CacheFile.GetPkgCache();
if (unlikely(Cache == NULL))
return false;
- // Generate the base argument list for dpkg
- std::vector<const char *> Args;
- string Tmp = _config->Find("Dir::Bin::dpkg","dpkg");
- {
- string const dpkgChrootDir = _config->FindDir("DPkg::Chroot-Directory", "/");
- size_t dpkgChrootLen = dpkgChrootDir.length();
- if (dpkgChrootDir != "/" && Tmp.find(dpkgChrootDir) == 0)
- {
- if (dpkgChrootDir[dpkgChrootLen - 1] == '/')
- --dpkgChrootLen;
- Tmp = Tmp.substr(dpkgChrootLen);
- }
- }
- Args.push_back(Tmp.c_str());
-
- // Stick in any custom dpkg options
- Configuration::Item const *Opts = _config->Tree("DPkg::Options");
- if (Opts != 0)
- {
- Opts = Opts->Child;
- for (; Opts != 0; Opts = Opts->Next)
- {
- if (Opts->Value.empty() == true)
- continue;
- Args.push_back(Opts->Value.c_str());
- }
- }
-
- size_t const BaseArgs = Args.size();
- // we need to detect if we can qualify packages with the architecture or not
- Args.push_back("--assert-multi-arch");
- Args.push_back(NULL);
-
-
- pid_t dpkgAssertMultiArch = ExecFork();
- if (dpkgAssertMultiArch == 0)
- {
- std::string const chrootDir = _config->FindDir("DPkg::Chroot-Directory");
- // redirect everything to the ultimate sink as we only need the exit-status
- int const nullfd = open("/dev/null", O_RDONLY);
- dup2(nullfd, STDIN_FILENO);
- dup2(nullfd, STDOUT_FILENO);
- dup2(nullfd, STDERR_FILENO);
- if (chrootDir != "/" && chroot(chrootDir.c_str()) != 0 && chdir("/") != 0)
- _error->WarningE("getArchitecture", "Couldn't chroot into %s for dpkg --assert-multi-arch", chrootDir.c_str());
- execvp(Args[0], (char**) &Args[0]);
- _error->WarningE("dpkgGo", "Can't detect if dpkg supports multi-arch!");
- _exit(2);
- }
-
- APT::PackageList pkgset = APT::PackageList::FromCommandLine(CacheFile, CmdL.FileList + 1);
+ APT::VersionVector pkgset = APT::VersionVector::FromCommandLine(CacheFile, CmdL.FileList + 1, APT::CacheSetHelper::INSTCAND);
if (pkgset.empty() == true)
return _error->Error(_("No packages found"));
- bool const MarkHold = strcasecmp(CmdL.FileList[0],"hold") == 0;
-
- for (APT::PackageList::iterator Pkg = pkgset.begin(); Pkg != pkgset.end();)
+ APT::StateChanges marks;
+ if (strcasecmp(CmdL.FileList[0], "hold") == 0 || strcasecmp(CmdL.FileList[0], "unhold") == 0)
{
- if ((Pkg->SelectedState == pkgCache::State::Hold) == MarkHold)
- {
- if (MarkHold == true)
- ioprintf(c1out,_("%s was already set on hold.\n"), Pkg.FullName(true).c_str());
- else
- ioprintf(c1out,_("%s was already not hold.\n"), Pkg.FullName(true).c_str());
- Pkg = pkgset.erase(Pkg, true);
- }
- else
- ++Pkg;
- }
-
- bool dpkgMultiArch = false;
- if (dpkgAssertMultiArch > 0)
- {
- int Status = 0;
- while (waitpid(dpkgAssertMultiArch, &Status, 0) != dpkgAssertMultiArch)
- {
- if (errno == EINTR)
- continue;
- _error->WarningE("dpkgGo", _("Waited for %s but it wasn't there"), "dpkg --assert-multi-arch");
- break;
- }
- if (WIFEXITED(Status) == true && WEXITSTATUS(Status) == 0)
- dpkgMultiArch = true;
- }
-
- if (pkgset.empty() == true)
- return true;
-
- if (_config->FindB("APT::Mark::Simulate", false) == true)
- {
- for (APT::PackageList::iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg)
- {
- if (MarkHold == false)
- ioprintf(c1out,_("%s set on hold.\n"), Pkg.FullName(true).c_str());
- else
- ioprintf(c1out,_("Canceled hold on %s.\n"), Pkg.FullName(true).c_str());
- }
- return true;
- }
-
- Args.erase(Args.begin() + BaseArgs, Args.end());
- Args.push_back("--set-selections");
- Args.push_back(NULL);
-
- int external[2] = {-1, -1};
- if (pipe(external) != 0)
- return _error->WarningE("DoHold", "Can't create IPC pipe for dpkg --set-selections");
+ auto const part = std::stable_partition(pkgset.begin(), pkgset.end(),
+ [](pkgCache::VerIterator const &V) { return V.ParentPkg()->SelectedState == pkgCache::State::Hold; });
+
+ bool const MarkHold = strcasecmp(CmdL.FileList[0],"hold") == 0;
+ auto const doneBegin = MarkHold ? pkgset.begin() : part;
+ auto const doneEnd = MarkHold ? part : pkgset.end();
+ std::for_each(doneBegin, doneEnd, [&MarkHold](pkgCache::VerIterator const &V) {
+ if (MarkHold == true)
+ ioprintf(c1out, _("%s was already set on hold.\n"), V.ParentPkg().FullName(true).c_str());
+ else
+ ioprintf(c1out, _("%s was already not hold.\n"), V.ParentPkg().FullName(true).c_str());
+ });
+
+ if (doneBegin == pkgset.begin() && doneEnd == pkgset.end())
+ return true;
- pid_t dpkgSelection = ExecFork();
- if (dpkgSelection == 0)
- {
- close(external[1]);
- std::string const chrootDir = _config->FindDir("DPkg::Chroot-Directory");
- if (chrootDir != "/" && chroot(chrootDir.c_str()) != 0 && chdir("/") != 0)
- _error->WarningE("getArchitecture", "Couldn't chroot into %s for dpkg --set-selections", chrootDir.c_str());
- int const nullfd = open("/dev/null", O_RDONLY);
- dup2(external[0], STDIN_FILENO);
- dup2(nullfd, STDOUT_FILENO);
- dup2(nullfd, STDERR_FILENO);
- execvp(Args[0], (char**) &Args[0]);
- _error->WarningE("dpkgGo", "Can't detect if dpkg supports multi-arch!");
- _exit(2);
+ auto const changeBegin = MarkHold ? part : pkgset.begin();
+ auto const changeEnd = MarkHold ? pkgset.end() : part;
+ std::move(changeBegin, changeEnd, std::back_inserter(MarkHold ? marks.Hold() : marks.Unhold()));
}
-
- FILE* dpkg = fdopen(external[1], "w");
- for (APT::PackageList::iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg)
+ else
{
- if (dpkgMultiArch == false)
- fprintf(dpkg, "%s", Pkg.FullName(true).c_str());
- else
- {
- if (Pkg->CurrentVer != 0)
- fprintf(dpkg, "%s:%s", Pkg.Name(), Pkg.CurrentVer().Arch());
- else if (Pkg.VersionList().end() == false)
- fprintf(dpkg, "%s:%s", Pkg.Name(), Pkg.VersionList().Arch());
- else
- fprintf(dpkg, "%s", Pkg.FullName(false).c_str());
- }
-
- if (MarkHold == true)
- {
- fprintf(dpkg, " hold\n");
- ioprintf(c1out,_("%s set on hold.\n"), Pkg.FullName(true).c_str());
- }
- else
- {
- fprintf(dpkg, " install\n");
- ioprintf(c1out,_("Canceled hold on %s.\n"), Pkg.FullName(true).c_str());
- }
+ // FIXME: Maybe show a message for unchanged states here as well?
+ if (strcasecmp(CmdL.FileList[0], "purge") == 0)
+ std::swap(marks.Purge(), pkgset);
+ else if (strcasecmp(CmdL.FileList[0], "deinstall") == 0 || strcasecmp(CmdL.FileList[0], "remove") == 0)
+ std::swap(marks.Remove(), pkgset);
+ else //if (strcasecmp(CmdL.FileList[0], "install") == 0)
+ std::swap(marks.Install(), pkgset);
}
- fclose(dpkg);
+ pkgset.clear();
- if (dpkgSelection > 0)
+ bool success = true;
+ if (_config->FindB("APT::Mark::Simulate", false) == false)
{
- int Status = 0;
- while (waitpid(dpkgSelection, &Status, 0) != dpkgSelection)
- {
- if (errno == EINTR)
- continue;
- _error->WarningE("dpkgGo", _("Waited for %s but it wasn't there"), "dpkg --set-selection");
- break;
- }
- if (WIFEXITED(Status) == true && WEXITSTATUS(Status) == 0)
- return true;
+ success = marks.Save();
+ if (success == false)
+ _error->Error(_("Executing dpkg failed. Are you root?"));
}
- return _error->Error(_("Executing dpkg failed. Are you root?"));
+ for (auto Ver : marks.Hold())
+ ioprintf(c1out,_("%s set on hold.\n"), Ver.ParentPkg().FullName(true).c_str());
+ for (auto Ver : marks.Unhold())
+ ioprintf(c1out,_("Canceled hold on %s.\n"), Ver.ParentPkg().FullName(true).c_str());
+ for (auto Ver : marks.Purge())
+ ioprintf(c1out,_("Selected %s for purge.\n"), Ver.ParentPkg().FullName(true).c_str());
+ for (auto Ver : marks.Remove())
+ ioprintf(c1out,_("Selected %s for removal.\n"), Ver.ParentPkg().FullName(true).c_str());
+ for (auto Ver : marks.Install())
+ ioprintf(c1out,_("Selected %s for installation.\n"), Ver.ParentPkg().FullName(true).c_str());
+ return success;
}
/*}}}*/
-/* ShowHold - show packages set on hold in dpkg status {{{*/
-static bool ShowHold(CommandLine &CmdL)
+static bool ShowSelection(CommandLine &CmdL) /*{{{*/
{
pkgCacheFile CacheFile;
pkgCache *Cache = CacheFile.GetPkgCache();
if (unlikely(Cache == NULL))
return false;
+ pkgCache::State::PkgSelectedState selector;
+ if (strncasecmp(CmdL.FileList[0], "showpurge", strlen("showpurge")) == 0)
+ selector = pkgCache::State::Purge;
+ else if (strncasecmp(CmdL.FileList[0], "showdeinstall", strlen("showdeinstall")) == 0 ||
+ strncasecmp(CmdL.FileList[0], "showremove", strlen("showremove")) == 0)
+ selector = pkgCache::State::DeInstall;
+ else if (strncasecmp(CmdL.FileList[0], "showhold", strlen("showhold")) == 0)
+ selector = pkgCache::State::Hold;
+ else //if (strcasecmp(CmdL.FileList[0], "showinstall", strlen("showinstall")) == 0)
+ selector = pkgCache::State::Install;
+
std::vector<string> packages;
if (CmdL.FileList[1] == 0)
{
packages.reserve(50); // how many holds are realistic? I hope just a few…
for (pkgCache::PkgIterator P = Cache->PkgBegin(); P.end() == false; ++P)
- if (P->SelectedState == pkgCache::State::Hold)
+ if (P->SelectedState == selector)
packages.push_back(P.FullName(true));
}
else
APT::PackageSet pkgset = APT::PackageSet::FromCommandLine(CacheFile, CmdL.FileList + 1, helper);
packages.reserve(pkgset.size());
for (APT::PackageSet::const_iterator P = pkgset.begin(); P != pkgset.end(); ++P)
- if (P->SelectedState == pkgCache::State::Hold)
+ if (P->SelectedState == selector)
packages.push_back(P.FullName(true));
}
return true;
}
/*}}}*/
-// ShowHelp - Show a help screen /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-static bool ShowHelp(CommandLine &CmdL)
+static bool ShowHelp(CommandLine &) /*{{{*/
{
- ioprintf(cout,_("%s %s for %s compiled on %s %s\n"),PACKAGE,PACKAGE_VERSION,
- COMMON_ARCH,__DATE__,__TIME__);
-
- cout <<
+ std::cout <<
_("Usage: apt-mark [options] {auto|manual} pkg1 [pkg2 ...]\n"
"\n"
"apt-mark is a simple command line interface for marking packages\n"
- "as manually or automatically installed. It can also list marks.\n"
- "\n"
- "Commands:\n"
- " auto - Mark the given packages as automatically installed\n"
- " manual - Mark the given packages as manually installed\n"
- " hold - Mark a package as held back\n"
- " unhold - Unset a package set as held back\n"
- " showauto - Print the list of automatically installed packages\n"
- " showmanual - Print the list of manually installed packages\n"
- " showhold - Print the list of package on hold\n"
- "\n"
- "Options:\n"
- " -h This help text.\n"
- " -q Loggable output - no progress indicator\n"
- " -qq No output except for errors\n"
- " -s No-act. Just prints what would be done.\n"
- " -f read/write auto/manual marking in the given file\n"
- " -c=? Read this configuration file\n"
- " -o=? Set an arbitrary configuration option, eg -o dir::cache=/tmp\n"
- "See the apt-mark(8) and apt.conf(5) manual pages for more information.")
- << std::endl;
+ "as manually or automatically installed. It can also be used to\n"
+ "manipulate the dpkg(1) selection states of packages, and to list\n"
+ "all packages with or without a certain marking.\n");
return true;
}
/*}}}*/
+static std::vector<aptDispatchWithHelp> GetCommands() /*{{{*/
+{
+ return {
+ {"auto",&DoAuto, _("Mark the given packages as automatically installed")},
+ {"manual",&DoAuto, _("Mark the given packages as manually installed")},
+ {"hold",&DoSelection, _("Mark a package as held back")},
+ {"unhold",&DoSelection, _("Unset a package set as held back")},
+ {"install",&DoSelection, nullptr},
+ {"remove",&DoSelection, nullptr}, // dpkg uses deinstall, but we use remove everywhere else
+ {"deinstall",&DoSelection, nullptr},
+ {"purge",&DoSelection, nullptr},
+ {"showauto",&ShowAuto, _("Print the list of automatically installed packages")},
+ {"showmanual",&ShowAuto, _("Print the list of manually installed packages")},
+ {"showhold",&ShowSelection, _("Print the list of package on hold")}, {"showholds",&ShowSelection, nullptr},
+ {"showinstall",&ShowSelection, nullptr}, {"showinstalls",&ShowSelection, nullptr},
+ {"showdeinstall",&ShowSelection, nullptr}, {"showdeinstalls",&ShowSelection, nullptr},
+ {"showremove",&ShowSelection, nullptr}, {"showremoves",&ShowSelection, nullptr},
+ {"showpurge",&ShowSelection, nullptr}, {"showpurges",&ShowSelection, nullptr},
+ // obsolete commands for compatibility
+ {"markauto", &DoMarkAuto, nullptr},
+ {"unmarkauto", &DoMarkAuto, nullptr},
+ {nullptr, nullptr, nullptr}
+ };
+}
+ /*}}}*/
int main(int argc,const char *argv[]) /*{{{*/
{
- CommandLine::Dispatch Cmds[] = {{"help",&ShowHelp},
- {"auto",&DoAuto},
- {"manual",&DoAuto},
- {"hold",&DoHold},
- {"unhold",&DoHold},
- {"showauto",&ShowAuto},
- {"showmanual",&ShowAuto},
- {"showhold",&ShowHold},
- // be nice and forgive the typo
- {"showholds",&ShowHold},
- // be nice and forgive it as it is technical right
- {"install",&DoHold},
- // obsolete commands for compatibility
- {"markauto", &DoMarkAuto},
- {"unmarkauto", &DoMarkAuto},
- {0,0}};
-
- std::vector<CommandLine::Args> Args = getCommandArgs("apt-mark", CommandLine::GetCommand(Cmds, argc, argv));
-
- // Set up gettext support
- setlocale(LC_ALL,"");
- textdomain(PACKAGE);
-
- // Parse the command line and initialize the package library
- CommandLine CmdL(Args.data(),_config);
- if (pkgInitConfig(*_config) == false ||
- CmdL.Parse(argc,argv) == false ||
- pkgInitSystem(*_config,_system) == false)
- {
- if (_config->FindB("version") == true)
- ShowHelp(CmdL);
- _error->DumpErrors();
- return 100;
- }
+ InitLocale();
- // See if the help should be shown
- if (_config->FindB("help") == true ||
- _config->FindB("version") == true ||
- CmdL.FileSize() == 0)
- {
- ShowHelp(CmdL);
- return 0;
- }
+ CommandLine CmdL;
+ auto const Cmds = ParseCommandLine(CmdL, APT_CMD::APT_MARK, &_config, &_system, argc, argv, &ShowHelp, &GetCommands);
- // Deal with stdout not being a tty
- if (!isatty(STDOUT_FILENO) && _config->FindI("quiet", -1) == -1)
- _config->Set("quiet","1");
-
- // Setup the output streams
- c0out.rdbuf(cout.rdbuf());
- c1out.rdbuf(cout.rdbuf());
- c2out.rdbuf(cout.rdbuf());
- if (_config->FindI("quiet",0) > 0)
- c0out.rdbuf(devnull.rdbuf());
- if (_config->FindI("quiet",0) > 1)
- c1out.rdbuf(devnull.rdbuf());
-
- // Match the operation
- CmdL.DispatchArg(Cmds);
-
- // Print any errors or warnings found during parsing
- bool const Errors = _error->PendingError();
- if (_config->FindI("quiet",0) > 0)
- _error->DumpErrors();
- else
- _error->DumpErrors(GlobalError::DEBUG);
- return Errors == true ? 100 : 0;
+ InitOutput();
+
+ return DispatchCommandLine(CmdL, Cmds);
}
/*}}}*/