summaryrefslogtreecommitdiffstats
path: root/cmdline
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--cmdline/CMakeLists.txt59
-rw-r--r--cmdline/apt-cache.cc1145
-rw-r--r--cmdline/apt-cdrom.cc235
-rw-r--r--cmdline/apt-config.cc144
-rw-r--r--cmdline/apt-dump-solver.cc184
-rw-r--r--cmdline/apt-extracttemplates.cc336
-rw-r--r--cmdline/apt-extracttemplates.h49
-rw-r--r--cmdline/apt-get.cc450
-rw-r--r--cmdline/apt-helper.cc334
-rw-r--r--cmdline/apt-internal-planner.cc195
-rw-r--r--cmdline/apt-internal-solver.cc214
-rw-r--r--cmdline/apt-key.in831
-rw-r--r--cmdline/apt-mark.cc476
-rwxr-xr-xcmdline/apt-report-mirror-failure29
-rw-r--r--cmdline/apt-sortpkgs.cc166
-rw-r--r--cmdline/apt.cc123
16 files changed, 4970 insertions, 0 deletions
diff --git a/cmdline/CMakeLists.txt b/cmdline/CMakeLists.txt
new file mode 100644
index 0000000..91bf9bb
--- /dev/null
+++ b/cmdline/CMakeLists.txt
@@ -0,0 +1,59 @@
+# Create the executable tasks
+add_executable(apt apt.cc)
+add_executable(apt-cache apt-cache.cc)
+add_executable(apt-get apt-get.cc)
+add_executable(apt-mark apt-mark.cc)
+add_executable(apt-config apt-config.cc)
+add_executable(apt-cdrom apt-cdrom.cc)
+add_executable(apt-helper apt-helper.cc)
+add_executable(apt-sortpkgs apt-sortpkgs.cc)
+add_executable(apt-extracttemplates apt-extracttemplates.cc)
+add_executable(apt-internal-solver apt-internal-solver.cc)
+add_executable(apt-dump-solver apt-dump-solver.cc)
+add_executable(apt-internal-planner apt-internal-planner.cc)
+add_vendor_file(OUTPUT apt-key
+ INPUT apt-key.in
+ MODE 755
+ VARIABLES keyring-filename
+ keyring-removed-filename
+ keyring-master-filename
+ keyring-uri keyring-package)
+
+
+# Link the executables against the libraries
+target_link_libraries(apt apt-pkg apt-private)
+target_link_libraries(apt-cache apt-pkg apt-private)
+target_link_libraries(apt-get apt-pkg apt-private)
+target_link_libraries(apt-config apt-pkg apt-private)
+target_link_libraries(apt-cdrom apt-pkg apt-private)
+target_link_libraries(apt-helper apt-pkg apt-private)
+target_link_libraries(apt-mark apt-pkg apt-private)
+target_link_libraries(apt-sortpkgs apt-pkg apt-private)
+target_link_libraries(apt-extracttemplates apt-pkg apt-private)
+target_link_libraries(apt-internal-solver apt-pkg apt-private)
+target_link_libraries(apt-dump-solver apt-pkg apt-private)
+target_link_libraries(apt-internal-planner apt-pkg apt-private)
+
+set_target_properties(apt-dump-solver
+ PROPERTIES RUNTIME_OUTPUT_DIRECTORY solvers
+ RUNTIME_OUTPUT_NAME dump)
+set_target_properties(apt-internal-solver
+ PROPERTIES RUNTIME_OUTPUT_DIRECTORY solvers
+ RUNTIME_OUTPUT_NAME apt)
+set_target_properties(apt-internal-planner
+ PROPERTIES RUNTIME_OUTPUT_DIRECTORY planners
+ RUNTIME_OUTPUT_NAME apt)
+
+# Install the executables
+install(TARGETS apt apt-cache apt-get apt-config apt-cdrom apt-mark apt-sortpkgs
+ apt-extracttemplates
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+install(TARGETS apt-helper RUNTIME DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/apt/)
+install(TARGETS apt-dump-solver apt-internal-solver RUNTIME DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/apt/solvers)
+install(TARGETS apt-internal-planner RUNTIME DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/apt/planners)
+
+add_links(${CMAKE_INSTALL_LIBEXECDIR}/apt/planners ../solvers/dump planners/dump)
+
+# Install the not-to-be-compiled programs
+INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/apt-key DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/cmdline/apt-cache.cc b/cmdline/apt-cache.cc
new file mode 100644
index 0000000..ffe6509
--- /dev/null
+++ b/cmdline/apt-cache.cc
@@ -0,0 +1,1145 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ apt-cache - Manages the cache files
+
+ apt-cache provides some functions fo manipulating the cache files.
+ It uses the command line interface common to all the APT tools.
+
+ Returns 100 on failure, 0 on success.
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/algorithms.h>
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/cacheset.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/depcache.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/indexfile.h>
+#include <apt-pkg/init.h>
+#include <apt-pkg/macros.h>
+#include <apt-pkg/metaindex.h>
+#include <apt-pkg/mmap.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/pkgrecords.h>
+#include <apt-pkg/pkgsystem.h>
+#include <apt-pkg/policy.h>
+#include <apt-pkg/progress.h>
+#include <apt-pkg/sourcelist.h>
+#include <apt-pkg/srcrecords.h>
+#include <apt-pkg/strutl.h>
+#include <apt-pkg/tagfile.h>
+#include <apt-pkg/version.h>
+
+#include <apt-private/private-cacheset.h>
+#include <apt-private/private-cmndline.h>
+#include <apt-private/private-depends.h>
+#include <apt-private/private-main.h>
+#include <apt-private/private-search.h>
+#include <apt-private/private-show.h>
+#include <apt-private/private-unmet.h>
+
+#include <algorithm>
+#include <cstring>
+#include <iomanip>
+#include <iostream>
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <regex.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+using namespace std;
+
+// DumpPackage - Show a dump of a package record /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+static bool DumpPackage(CommandLine &CmdL)
+{
+ pkgCacheFile CacheFile;
+ APT::CacheSetHelper helper(true, GlobalError::NOTICE);
+ APT::PackageList pkgset = APT::PackageList::FromCommandLine(CacheFile, CmdL.FileList + 1, helper);
+
+ for (APT::PackageList::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg)
+ {
+ cout << "Package: " << Pkg.FullName(true) << endl;
+ cout << "Versions: " << endl;
+ for (pkgCache::VerIterator Cur = Pkg.VersionList(); Cur.end() != true; ++Cur)
+ {
+ cout << Cur.VerStr();
+ for (pkgCache::VerFileIterator Vf = Cur.FileList(); Vf.end() == false; ++Vf)
+ cout << " (" << Vf.File().FileName() << ")";
+ cout << endl;
+ for (pkgCache::DescIterator D = Cur.DescriptionList(); D.end() == false; ++D)
+ {
+ cout << " Description Language: " << D.LanguageCode() << endl
+ << " File: " << D.FileList().File().FileName() << endl
+ << " MD5: " << D.md5() << endl;
+ }
+ cout << endl;
+ }
+
+ cout << endl;
+
+ cout << "Reverse Depends: " << endl;
+ for (pkgCache::DepIterator D = Pkg.RevDependsList(); D.end() != true; ++D)
+ {
+ cout << " " << D.ParentPkg().FullName(true) << ',' << D.TargetPkg().FullName(true);
+ if (D->Version != 0)
+ cout << ' ' << DeNull(D.TargetVer()) << endl;
+ else
+ cout << endl;
+ }
+
+ cout << "Dependencies: " << endl;
+ for (pkgCache::VerIterator Cur = Pkg.VersionList(); Cur.end() != true; ++Cur)
+ {
+ cout << Cur.VerStr() << " - ";
+ for (pkgCache::DepIterator Dep = Cur.DependsList(); Dep.end() != true; ++Dep)
+ cout << Dep.TargetPkg().FullName(true) << " (" << (int)Dep->CompareOp << " " << DeNull(Dep.TargetVer()) << ") ";
+ cout << endl;
+ }
+
+ cout << "Provides: " << endl;
+ for (pkgCache::VerIterator Cur = Pkg.VersionList(); Cur.end() != true; ++Cur)
+ {
+ cout << Cur.VerStr() << " - ";
+ for (pkgCache::PrvIterator Prv = Cur.ProvidesList(); Prv.end() != true; ++Prv)
+ cout << Prv.ParentPkg().FullName(true) << " (= " << (Prv->ProvideVersion == 0 ? "" : Prv.ProvideVersion()) << ") ";
+ cout << endl;
+ }
+ cout << "Reverse Provides: " << endl;
+ for (pkgCache::PrvIterator Prv = Pkg.ProvidesList(); Prv.end() != true; ++Prv)
+ cout << Prv.OwnerPkg().FullName(true) << " " << Prv.OwnerVer().VerStr() << " (= " << (Prv->ProvideVersion == 0 ? "" : Prv.ProvideVersion()) << ")"<< endl;
+ }
+
+ return true;
+}
+ /*}}}*/
+// ShowHashTableStats - Show stats about a hashtable /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+static map_pointer<pkgCache::Package> PackageNext(pkgCache::Package const * const P) { return P->NextPackage; }
+static map_pointer<pkgCache::Group> GroupNext(pkgCache::Group const * const G) { return G->Next; }
+template <class T>
+static void ShowHashTableStats(char const *const Type,
+ T *StartP,
+ map_pointer<T> *Hashtable,
+ unsigned long Size,
+ map_pointer<T> (*Next)(T const *const))
+{
+ // hashtable stats for the HashTable
+ unsigned long NumBuckets = Size;
+ unsigned long UsedBuckets = 0;
+ unsigned long UnusedBuckets = 0;
+ unsigned long LongestBucket = 0;
+ unsigned long ShortestBucket = NumBuckets;
+ unsigned long Entries = 0;
+ for (unsigned int i=0; i < NumBuckets; ++i)
+ {
+ T *P = StartP + Hashtable[i];
+ if(P == 0 || P == StartP)
+ {
+ ++UnusedBuckets;
+ continue;
+ }
+ ++UsedBuckets;
+ unsigned long ThisBucketSize = 0;
+ for (; P != StartP; P = StartP + Next(P))
+ ++ThisBucketSize;
+ Entries += ThisBucketSize;
+ LongestBucket = std::max(ThisBucketSize, LongestBucket);
+ ShortestBucket = std::min(ThisBucketSize, ShortestBucket);
+ }
+ cout << "Total buckets in " << Type << ": " << NumBuckets << std::endl;
+ cout << " Unused: " << UnusedBuckets << std::endl;
+ cout << " Used: " << UsedBuckets << std::endl;
+ cout << " Utilization: " << 100.0 * UsedBuckets/NumBuckets << "%" << std::endl;
+ cout << " Average entries: " << Entries/(double)UsedBuckets << std::endl;
+ cout << " Longest: " << LongestBucket << std::endl;
+ cout << " Shortest: " << ShortestBucket << std::endl;
+}
+ /*}}}*/
+// Stats - Dump some nice statistics /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+static bool Stats(CommandLine &CmdL)
+{
+ if (CmdL.FileSize() > 1) {
+ _error->Error(_("%s does not take any arguments"), "apt-cache stats");
+ return false;
+ }
+
+ pkgCacheFile CacheFile;
+ pkgCache *Cache = CacheFile.GetPkgCache();
+
+ if (unlikely(Cache == NULL))
+ return false;
+
+ cout << _("Total package names: ") << Cache->Head().GroupCount << " (" <<
+ SizeToStr(Cache->Head().GroupCount*Cache->Head().GroupSz) << ')' << endl
+ << _("Total package structures: ") << Cache->Head().PackageCount << " (" <<
+ SizeToStr(Cache->Head().PackageCount*Cache->Head().PackageSz) << ')' << endl;
+
+ int Normal = 0;
+ int Virtual = 0;
+ int NVirt = 0;
+ int DVirt = 0;
+ int Missing = 0;
+ for (pkgCache::PkgIterator I = Cache->PkgBegin(); I.end() != true; ++I)
+ {
+ if (I->VersionList != 0 && I->ProvidesList == 0)
+ {
+ Normal++;
+ continue;
+ }
+
+ if (I->VersionList != 0 && I->ProvidesList != 0)
+ {
+ NVirt++;
+ continue;
+ }
+
+ if (I->VersionList == 0 && I->ProvidesList != 0)
+ {
+ // Only 1 provides
+ if (I.ProvidesList()->NextProvides == 0)
+ {
+ DVirt++;
+ }
+ else
+ Virtual++;
+ continue;
+ }
+ if (I->VersionList == 0 && I->ProvidesList == 0)
+ {
+ Missing++;
+ continue;
+ }
+ }
+ cout << _(" Normal packages: ") << Normal << endl;
+ cout << _(" Pure virtual packages: ") << Virtual << endl;
+ cout << _(" Single virtual packages: ") << DVirt << endl;
+ cout << _(" Mixed virtual packages: ") << NVirt << endl;
+ cout << _(" Missing: ") << Missing << endl;
+
+ cout << _("Total distinct versions: ") << Cache->Head().VersionCount << " (" <<
+ SizeToStr(Cache->Head().VersionCount*Cache->Head().VersionSz) << ')' << endl;
+ cout << _("Total distinct descriptions: ") << Cache->Head().DescriptionCount << " (" <<
+ SizeToStr(Cache->Head().DescriptionCount*Cache->Head().DescriptionSz) << ')' << endl;
+ cout << _("Total dependencies: ") << Cache->Head().DependsCount << "/" << Cache->Head().DependsDataCount << " (" <<
+ SizeToStr((Cache->Head().DependsCount*Cache->Head().DependencySz) +
+ (Cache->Head().DependsDataCount*Cache->Head().DependencyDataSz)) << ')' << endl;
+ cout << _("Total ver/file relations: ") << Cache->Head().VerFileCount << " (" <<
+ SizeToStr(Cache->Head().VerFileCount*Cache->Head().VerFileSz) << ')' << endl;
+ cout << _("Total Desc/File relations: ") << Cache->Head().DescFileCount << " (" <<
+ SizeToStr(Cache->Head().DescFileCount*Cache->Head().DescFileSz) << ')' << endl;
+ cout << _("Total Provides mappings: ") << Cache->Head().ProvidesCount << " (" <<
+ SizeToStr(Cache->Head().ProvidesCount*Cache->Head().ProvidesSz) << ')' << endl;
+
+ // String list stats
+ std::set<map_stringitem_t> stritems;
+ for (pkgCache::GrpIterator G = Cache->GrpBegin(); G.end() == false; ++G)
+ stritems.insert(G->Name);
+ for (pkgCache::PkgIterator P = Cache->PkgBegin(); P.end() == false; ++P)
+ {
+ stritems.insert(P->Arch);
+ for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
+ {
+ if (V->VerStr != 0)
+ stritems.insert(V->VerStr);
+ if (V->Section != 0)
+ stritems.insert(V->Section);
+ stritems.insert(V->SourcePkgName);
+ stritems.insert(V->SourceVerStr);
+ for (pkgCache::DepIterator D = V.DependsList(); D.end() == false; ++D)
+ {
+ if (D->Version != 0)
+ stritems.insert(D->Version);
+ }
+ for (pkgCache::DescIterator D = V.DescriptionList(); D.end() == false; ++D)
+ {
+ stritems.insert(D->md5sum);
+ stritems.insert(D->language_code);
+ }
+ }
+ for (pkgCache::PrvIterator Prv = P.ProvidesList(); Prv.end() == false; ++Prv)
+ {
+ if (Prv->ProvideVersion != 0)
+ stritems.insert(Prv->ProvideVersion);
+ }
+ }
+ for (pkgCache::RlsFileIterator F = Cache->RlsFileBegin(); F != Cache->RlsFileEnd(); ++F)
+ {
+ stritems.insert(F->FileName);
+ stritems.insert(F->Archive);
+ stritems.insert(F->Codename);
+ stritems.insert(F->Version);
+ stritems.insert(F->Origin);
+ stritems.insert(F->Label);
+ stritems.insert(F->Site);
+ }
+ for (pkgCache::PkgFileIterator F = Cache->FileBegin(); F != Cache->FileEnd(); ++F)
+ {
+ stritems.insert(F->FileName);
+ stritems.insert(F->Architecture);
+ stritems.insert(F->Component);
+ stritems.insert(F->IndexType);
+ }
+
+ unsigned long Size = 0;
+ for (std::set<map_stringitem_t>::const_iterator i = stritems.begin(); i != stritems.end(); ++i)
+ Size += strlen(Cache->StrP + *i) + 1;
+ cout << _("Total globbed strings: ") << stritems.size() << " (" << SizeToStr(Size) << ')' << endl;
+ stritems.clear();
+
+ unsigned long Slack = 0;
+ for (int I = 0; I != 7; I++)
+ Slack += Cache->Head().Pools[I].ItemSize*Cache->Head().Pools[I].Count;
+ cout << _("Total slack space: ") << SizeToStr(Slack) << endl;
+
+ unsigned long Total = 0;
+#define APT_CACHESIZE(X,Y) (Cache->Head().X * Cache->Head().Y)
+ Total = Slack + Size +
+ APT_CACHESIZE(GroupCount, GroupSz) +
+ APT_CACHESIZE(PackageCount, PackageSz) +
+ APT_CACHESIZE(VersionCount, VersionSz) +
+ APT_CACHESIZE(DescriptionCount, DescriptionSz) +
+ APT_CACHESIZE(DependsCount, DependencySz) +
+ APT_CACHESIZE(DependsDataCount, DependencyDataSz) +
+ APT_CACHESIZE(ReleaseFileCount, ReleaseFileSz) +
+ APT_CACHESIZE(PackageFileCount, PackageFileSz) +
+ APT_CACHESIZE(VerFileCount, VerFileSz) +
+ APT_CACHESIZE(DescFileCount, DescFileSz) +
+ APT_CACHESIZE(ProvidesCount, ProvidesSz) +
+ (2 * Cache->Head().GetHashTableSize() * sizeof(map_id_t));
+ cout << _("Total space accounted for: ") << SizeToStr(Total) << endl;
+#undef APT_CACHESIZE
+
+ // hashtable stats
+ ShowHashTableStats<pkgCache::Package>("PkgHashTable", Cache->PkgP, Cache->Head().PkgHashTableP(), Cache->Head().GetHashTableSize(), PackageNext);
+ ShowHashTableStats<pkgCache::Group>("GrpHashTable", Cache->GrpP, Cache->Head().GrpHashTableP(), Cache->Head().GetHashTableSize(), GroupNext);
+
+ return true;
+}
+ /*}}}*/
+// Dump - show everything /*{{{*/
+// ---------------------------------------------------------------------
+/* This is worthless except fer debugging things */
+static bool Dump(CommandLine &)
+{
+ pkgCacheFile CacheFile;
+ pkgCache *Cache = CacheFile.GetPkgCache();
+ if (unlikely(Cache == NULL))
+ return false;
+
+ std::cout << "Using Versioning System: " << Cache->VS->Label << std::endl;
+
+ for (pkgCache::PkgIterator P = Cache->PkgBegin(); P.end() == false; ++P)
+ {
+ std::cout << "Package: " << P.FullName(true) << std::endl;
+ for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
+ {
+ std::cout << " Version: " << V.VerStr() << std::endl;
+ std::cout << " File: " << V.FileList().File().FileName() << std::endl;
+ for (pkgCache::DepIterator D = V.DependsList(); D.end() == false; ++D)
+ std::cout << " Depends: " << D.TargetPkg().FullName(true) << ' ' <<
+ DeNull(D.TargetVer()) << std::endl;
+ for (pkgCache::DescIterator D = V.DescriptionList(); D.end() == false; ++D)
+ {
+ std::cout << " Description Language: " << D.LanguageCode() << std::endl
+ << " File: " << D.FileList().File().FileName() << std::endl
+ << " MD5: " << D.md5() << std::endl;
+ }
+ }
+ }
+
+ for (pkgCache::PkgFileIterator F = Cache->FileBegin(); F.end() == false; ++F)
+ {
+ std::cout << "File: " << F.FileName() << std::endl;
+ std::cout << " Type: " << F.IndexType() << std::endl;
+ std::cout << " Size: " << F->Size << std::endl;
+ std::cout << " ID: " << F->ID << std::endl;
+ std::cout << " Flags: " << F->Flags << std::endl;
+ std::cout << " Time: " << TimeRFC1123(F->mtime, true) << std::endl;
+ std::cout << " Archive: " << DeNull(F.Archive()) << std::endl;
+ std::cout << " Component: " << DeNull(F.Component()) << std::endl;
+ std::cout << " Version: " << DeNull(F.Version()) << std::endl;
+ std::cout << " Origin: " << DeNull(F.Origin()) << std::endl;
+ std::cout << " Site: " << DeNull(F.Site()) << std::endl;
+ std::cout << " Label: " << DeNull(F.Label()) << std::endl;
+ std::cout << " Architecture: " << DeNull(F.Architecture()) << std::endl;
+ }
+
+ return true;
+}
+ /*}}}*/
+// DumpAvail - Print out the available list /*{{{*/
+// ---------------------------------------------------------------------
+/* This is needed to make dpkg --merge happy.. I spent a bit of time to
+ make this run really fast, perhaps I went a little overboard.. */
+static bool DumpAvail(CommandLine &)
+{
+ pkgCacheFile CacheFile;
+ pkgCache *Cache = CacheFile.GetPkgCache();
+ if (unlikely(Cache == NULL || CacheFile.BuildPolicy() == false))
+ return false;
+
+ auto const Count = Cache->HeaderP->PackageCount+1;
+ pkgCache::VerFile **VFList = new pkgCache::VerFile *[Count];
+ memset(VFList,0,sizeof(*VFList)*Count);
+
+ // Map versions that we want to write out onto the VerList array.
+ for (pkgCache::PkgIterator P = Cache->PkgBegin(); P.end() == false; ++P)
+ {
+ if (P->VersionList == 0)
+ continue;
+
+ /* Find the proper version to use. If the policy says there are no
+ possible selections we return the installed version, if available..
+ This prevents dselect from making it obsolete. */
+ pkgCache::VerIterator V = CacheFile.GetPolicy()->GetCandidateVer(P);
+ if (V.end() == true)
+ {
+ if (P->CurrentVer == 0)
+ continue;
+ V = P.CurrentVer();
+ }
+
+ pkgCache::VerFileIterator VF = V.FileList();
+ for (; VF.end() == false ; ++VF)
+ if ((VF.File()->Flags & pkgCache::Flag::NotSource) == 0)
+ break;
+
+ /* Okay, here we have a bit of a problem.. The policy has selected the
+ currently installed package - however it only exists in the
+ status file.. We need to write out something or dselect will mark
+ the package as obsolete! Thus we emit the status file entry, but
+ below we remove the status line to make it valid for the
+ available file. However! We only do this if their do exist *any*
+ non-source versions of the package - that way the dselect obsolete
+ handling works OK. */
+ if (VF.end() == true)
+ {
+ for (pkgCache::VerIterator Cur = P.VersionList(); Cur.end() != true; ++Cur)
+ {
+ for (VF = Cur.FileList(); VF.end() == false; ++VF)
+ {
+ if ((VF.File()->Flags & pkgCache::Flag::NotSource) == 0)
+ {
+ VF = V.FileList();
+ break;
+ }
+ }
+
+ if (VF.end() == false)
+ break;
+ }
+ }
+
+ VFList[P->ID] = VF;
+ }
+
+ LocalitySort(VFList,Count,sizeof(*VFList));
+
+ std::vector<pkgTagSection::Tag> RW;
+ RW.push_back(pkgTagSection::Tag::Remove("Status"));
+ RW.push_back(pkgTagSection::Tag::Remove("Config-Version"));
+ FileFd stdoutfd;
+ stdoutfd.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly, false);
+
+ // Iterate over all the package files and write them out.
+ char *Buffer = new char[Cache->HeaderP->MaxVerFileSize+10];
+ for (pkgCache::VerFile **J = VFList; *J != 0;)
+ {
+ pkgCache::PkgFileIterator File(*Cache, Cache->PkgFileP + (*J)->File);
+ // FIXME: Add support for volatile/with-source files
+ FileFd PkgF(File.FileName(),FileFd::ReadOnly, FileFd::Extension);
+ if (_error->PendingError() == true)
+ break;
+
+ /* Write all of the records from this package file, since we
+ already did locality sorting we can now just seek through the
+ file in read order. We apply 1 more optimization here, since often
+ there will be < 1 byte gaps between records (for the \n) we read that
+ into the next buffer and offset a bit.. */
+ unsigned long Pos = 0;
+ for (; *J != 0; J++)
+ {
+ if (Cache->PkgFileP + (*J)->File != File)
+ break;
+
+ const pkgCache::VerFile &VF = **J;
+
+ // Read the record and then write it out again.
+ unsigned long Jitter = VF.Offset - Pos;
+ if (Jitter > 8)
+ {
+ if (PkgF.Seek(VF.Offset) == false)
+ break;
+ Jitter = 0;
+ }
+
+ if (PkgF.Read(Buffer,VF.Size + Jitter) == false)
+ break;
+ Buffer[VF.Size + Jitter] = '\n';
+
+ // See above..
+ if ((File->Flags & pkgCache::Flag::NotSource) == pkgCache::Flag::NotSource)
+ {
+ pkgTagSection Tags;
+ if (Tags.Scan(Buffer+Jitter,VF.Size+1) == false ||
+ Tags.Write(stdoutfd, NULL, RW) == false ||
+ stdoutfd.Write("\n", 1) == false)
+ {
+ _error->Error("Internal Error, Unable to parse a package record");
+ break;
+ }
+ }
+ else
+ {
+ if (stdoutfd.Write(Buffer + Jitter, VF.Size + 1) == false)
+ break;
+ }
+
+ Pos = VF.Offset + VF.Size;
+ }
+
+ if (_error->PendingError() == true)
+ break;
+ }
+
+ delete [] Buffer;
+ delete [] VFList;
+ return !_error->PendingError();
+}
+ /*}}}*/
+// xvcg - Generate a graph for xvcg /*{{{*/
+// ---------------------------------------------------------------------
+// Code contributed from Junichi Uekawa <dancer@debian.org> on 20 June 2002.
+
+static bool XVcg(CommandLine &CmdL)
+{
+ pkgCacheFile CacheFile;
+ pkgCache *Cache = CacheFile.GetPkgCache();
+ if (unlikely(Cache == NULL))
+ return false;
+
+ bool GivenOnly = _config->FindB("APT::Cache::GivenOnly",false);
+
+ /* Normal packages are boxes
+ Pure Provides are triangles
+ Mixed are diamonds
+ rhomb are missing packages*/
+ const char *Shapes[] = {"ellipse","triangle","box","rhomb"};
+
+ /* Initialize the list of packages to show.
+ 1 = To Show
+ 2 = To Show no recurse
+ 3 = Emitted no recurse
+ 4 = Emitted
+ 0 = None */
+ enum States {None=0, ToShow, ToShowNR, DoneNR, Done};
+ enum TheFlags {ForceNR=(1<<0)};
+ auto PackageCount = Cache->Head().PackageCount;
+ unsigned char *Show = new unsigned char[PackageCount];
+ unsigned char *Flags = new unsigned char[PackageCount];
+ unsigned char *ShapeMap = new unsigned char[PackageCount];
+
+ // Show everything if no arguments given
+ if (CmdL.FileList[1] == 0)
+ for (decltype(PackageCount) I = 0; I != PackageCount; ++I)
+ Show[I] = ToShow;
+ else
+ for (decltype(PackageCount) I = 0; I != PackageCount; ++I)
+ Show[I] = None;
+ memset(Flags,0,sizeof(*Flags)*PackageCount);
+
+ // Map the shapes
+ for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); Pkg.end() == false; ++Pkg)
+ {
+ if (Pkg->VersionList == 0)
+ {
+ // Missing
+ if (Pkg->ProvidesList == 0)
+ ShapeMap[Pkg->ID] = 0;
+ else
+ ShapeMap[Pkg->ID] = 1;
+ }
+ else
+ {
+ // Normal
+ if (Pkg->ProvidesList == 0)
+ ShapeMap[Pkg->ID] = 2;
+ else
+ ShapeMap[Pkg->ID] = 3;
+ }
+ }
+
+ // Load the list of packages from the command line into the show list
+ APT::CacheSetHelper helper(true, GlobalError::NOTICE);
+ std::list<APT::CacheSetHelper::PkgModifier> mods;
+ mods.push_back(APT::CacheSetHelper::PkgModifier(0, ",", APT::CacheSetHelper::PkgModifier::POSTFIX));
+ mods.push_back(APT::CacheSetHelper::PkgModifier(1, "^", APT::CacheSetHelper::PkgModifier::POSTFIX));
+ std::map<unsigned short, APT::PackageSet> pkgsets =
+ APT::PackageSet::GroupedFromCommandLine(CacheFile, CmdL.FileList + 1, mods, 0, helper);
+
+ for (APT::PackageSet::const_iterator Pkg = pkgsets[0].begin();
+ Pkg != pkgsets[0].end(); ++Pkg)
+ Show[Pkg->ID] = ToShow;
+ for (APT::PackageSet::const_iterator Pkg = pkgsets[1].begin();
+ Pkg != pkgsets[1].end(); ++Pkg)
+ {
+ Show[Pkg->ID] = ToShow;
+ Flags[Pkg->ID] |= ForceNR;
+ }
+
+ // Little header
+ cout << "graph: { title: \"packages\"" << endl <<
+ "xmax: 700 ymax: 700 x: 30 y: 30" << endl <<
+ "layout_downfactor: 8" << endl;
+
+ bool Act = true;
+ while (Act == true)
+ {
+ Act = false;
+ for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); Pkg.end() == false; ++Pkg)
+ {
+ // See we need to show this package
+ if (Show[Pkg->ID] == None || Show[Pkg->ID] >= DoneNR)
+ continue;
+
+ //printf ("node: { title: \"%s\" label: \"%s\" }\n", Pkg.Name(), Pkg.Name());
+
+ // Colour as done
+ if (Show[Pkg->ID] == ToShowNR || (Flags[Pkg->ID] & ForceNR) == ForceNR)
+ {
+ // Pure Provides and missing packages have no deps!
+ if (ShapeMap[Pkg->ID] == 0 || ShapeMap[Pkg->ID] == 1)
+ Show[Pkg->ID] = Done;
+ else
+ Show[Pkg->ID] = DoneNR;
+ }
+ else
+ Show[Pkg->ID] = Done;
+ Act = true;
+
+ // No deps to map out
+ if (Pkg->VersionList == 0 || Show[Pkg->ID] == DoneNR)
+ continue;
+
+ pkgCache::VerIterator Ver = Pkg.VersionList();
+ for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false; ++D)
+ {
+ // See if anything can meet this dep
+ // Walk along the actual package providing versions
+ bool Hit = false;
+ pkgCache::PkgIterator DPkg = D.TargetPkg();
+ for (pkgCache::VerIterator I = DPkg.VersionList();
+ I.end() == false && Hit == false; ++I)
+ {
+ if (Cache->VS->CheckDep(I.VerStr(),D->CompareOp,D.TargetVer()) == true)
+ Hit = true;
+ }
+
+ // Follow all provides
+ for (pkgCache::PrvIterator I = DPkg.ProvidesList();
+ I.end() == false && Hit == false; ++I)
+ {
+ if (Cache->VS->CheckDep(I.ProvideVersion(),D->CompareOp,D.TargetVer()) == false)
+ Hit = true;
+ }
+
+
+ // Only graph critical deps
+ if (D.IsCritical() == true)
+ {
+ printf ("edge: { sourcename: \"%s\" targetname: \"%s\" class: 2 ",Pkg.FullName(true).c_str(), D.TargetPkg().FullName(true).c_str() );
+
+ // Colour the node for recursion
+ if (Show[D.TargetPkg()->ID] <= DoneNR)
+ {
+ /* If a conflicts does not meet anything in the database
+ then show the relation but do not recurse */
+ if (Hit == false && D.IsNegative() == true)
+ {
+ if (Show[D.TargetPkg()->ID] == None &&
+ Show[D.TargetPkg()->ID] != ToShow)
+ Show[D.TargetPkg()->ID] = ToShowNR;
+ }
+ else
+ {
+ if (GivenOnly == true && Show[D.TargetPkg()->ID] != ToShow)
+ Show[D.TargetPkg()->ID] = ToShowNR;
+ else
+ Show[D.TargetPkg()->ID] = ToShow;
+ }
+ }
+
+ // Edge colour
+ switch(D->Type)
+ {
+ case pkgCache::Dep::Conflicts:
+ printf("label: \"conflicts\" color: lightgreen }\n");
+ break;
+ case pkgCache::Dep::DpkgBreaks:
+ printf("label: \"breaks\" color: lightgreen }\n");
+ break;
+ case pkgCache::Dep::Obsoletes:
+ printf("label: \"obsoletes\" color: lightgreen }\n");
+ break;
+
+ case pkgCache::Dep::PreDepends:
+ printf("label: \"predepends\" color: blue }\n");
+ break;
+
+ default:
+ printf("}\n");
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Draw the box colours after the fact since we can not tell what colour
+ they should be until everything is finished drawing */
+ for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); Pkg.end() == false; ++Pkg)
+ {
+ if (Show[Pkg->ID] < DoneNR)
+ continue;
+
+ if (Show[Pkg->ID] == DoneNR)
+ printf("node: { title: \"%s\" label: \"%s\" color: orange shape: %s }\n", Pkg.FullName(true).c_str(), Pkg.FullName(true).c_str(),
+ Shapes[ShapeMap[Pkg->ID]]);
+ else
+ printf("node: { title: \"%s\" label: \"%s\" shape: %s }\n", Pkg.FullName(true).c_str(), Pkg.FullName(true).c_str(),
+ Shapes[ShapeMap[Pkg->ID]]);
+
+ }
+
+ delete[] Show;
+ delete[] Flags;
+ delete[] ShapeMap;
+
+ printf("}\n");
+ return true;
+}
+ /*}}}*/
+// Dotty - Generate a graph for Dotty /*{{{*/
+// ---------------------------------------------------------------------
+/* Dotty is the graphvis program for generating graphs. It is a fairly
+ simple queuing algorithm that just writes dependencies and nodes.
+ http://www.research.att.com/sw/tools/graphviz/ */
+static bool Dotty(CommandLine &CmdL)
+{
+ pkgCacheFile CacheFile;
+ pkgCache *Cache = CacheFile.GetPkgCache();
+ if (unlikely(Cache == NULL))
+ return false;
+
+ bool GivenOnly = _config->FindB("APT::Cache::GivenOnly",false);
+
+ /* Normal packages are boxes
+ Pure Provides are triangles
+ Mixed are diamonds
+ Hexagons are missing packages*/
+ const char *Shapes[] = {"hexagon","triangle","box","diamond"};
+
+ /* Initialize the list of packages to show.
+ 1 = To Show
+ 2 = To Show no recurse
+ 3 = Emitted no recurse
+ 4 = Emitted
+ 0 = None */
+ enum States {None=0, ToShow, ToShowNR, DoneNR, Done};
+ enum TheFlags {ForceNR=(1<<0)};
+ auto PackageCount = Cache->Head().PackageCount;
+ unsigned char *Show = new unsigned char[PackageCount];
+ unsigned char *Flags = new unsigned char[PackageCount];
+ unsigned char *ShapeMap = new unsigned char[PackageCount];
+
+ // Show everything if no arguments given
+ if (CmdL.FileList[1] == 0)
+ for (decltype(PackageCount) I = 0; I != PackageCount; ++I)
+ Show[I] = ToShow;
+ else
+ for (decltype(PackageCount) I = 0; I != PackageCount; ++I)
+ Show[I] = None;
+ memset(Flags,0,sizeof(*Flags)*PackageCount);
+
+ // Map the shapes
+ for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); Pkg.end() == false; ++Pkg)
+ {
+ if (Pkg->VersionList == 0)
+ {
+ // Missing
+ if (Pkg->ProvidesList == 0)
+ ShapeMap[Pkg->ID] = 0;
+ else
+ ShapeMap[Pkg->ID] = 1;
+ }
+ else
+ {
+ // Normal
+ if (Pkg->ProvidesList == 0)
+ ShapeMap[Pkg->ID] = 2;
+ else
+ ShapeMap[Pkg->ID] = 3;
+ }
+ }
+
+ // Load the list of packages from the command line into the show list
+ APT::CacheSetHelper helper(true, GlobalError::NOTICE);
+ std::list<APT::CacheSetHelper::PkgModifier> mods;
+ mods.push_back(APT::CacheSetHelper::PkgModifier(0, ",", APT::CacheSetHelper::PkgModifier::POSTFIX));
+ mods.push_back(APT::CacheSetHelper::PkgModifier(1, "^", APT::CacheSetHelper::PkgModifier::POSTFIX));
+ std::map<unsigned short, APT::PackageSet> pkgsets =
+ APT::PackageSet::GroupedFromCommandLine(CacheFile, CmdL.FileList + 1, mods, 0, helper);
+
+ for (APT::PackageSet::const_iterator Pkg = pkgsets[0].begin();
+ Pkg != pkgsets[0].end(); ++Pkg)
+ Show[Pkg->ID] = ToShow;
+ for (APT::PackageSet::const_iterator Pkg = pkgsets[1].begin();
+ Pkg != pkgsets[1].end(); ++Pkg)
+ {
+ Show[Pkg->ID] = ToShow;
+ Flags[Pkg->ID] |= ForceNR;
+ }
+
+ // Little header
+ printf("digraph packages {\n");
+ printf("concentrate=true;\n");
+ printf("size=\"30,40\";\n");
+
+ bool Act = true;
+ while (Act == true)
+ {
+ Act = false;
+ for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); Pkg.end() == false; ++Pkg)
+ {
+ // See we need to show this package
+ if (Show[Pkg->ID] == None || Show[Pkg->ID] >= DoneNR)
+ continue;
+
+ // Colour as done
+ if (Show[Pkg->ID] == ToShowNR || (Flags[Pkg->ID] & ForceNR) == ForceNR)
+ {
+ // Pure Provides and missing packages have no deps!
+ if (ShapeMap[Pkg->ID] == 0 || ShapeMap[Pkg->ID] == 1)
+ Show[Pkg->ID] = Done;
+ else
+ Show[Pkg->ID] = DoneNR;
+ }
+ else
+ Show[Pkg->ID] = Done;
+ Act = true;
+
+ // No deps to map out
+ if (Pkg->VersionList == 0 || Show[Pkg->ID] == DoneNR)
+ continue;
+
+ pkgCache::VerIterator Ver = Pkg.VersionList();
+ for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false; ++D)
+ {
+ // See if anything can meet this dep
+ // Walk along the actual package providing versions
+ bool Hit = false;
+ pkgCache::PkgIterator DPkg = D.TargetPkg();
+ for (pkgCache::VerIterator I = DPkg.VersionList();
+ I.end() == false && Hit == false; ++I)
+ {
+ if (Cache->VS->CheckDep(I.VerStr(),D->CompareOp,D.TargetVer()) == true)
+ Hit = true;
+ }
+
+ // Follow all provides
+ for (pkgCache::PrvIterator I = DPkg.ProvidesList();
+ I.end() == false && Hit == false; ++I)
+ {
+ if (Cache->VS->CheckDep(I.ProvideVersion(),D->CompareOp,D.TargetVer()) == false)
+ Hit = true;
+ }
+
+ // Only graph critical deps
+ if (D.IsCritical() == true)
+ {
+ printf("\"%s\" -> \"%s\"",Pkg.FullName(true).c_str(),D.TargetPkg().FullName(true).c_str());
+
+ // Colour the node for recursion
+ if (Show[D.TargetPkg()->ID] <= DoneNR)
+ {
+ /* If a conflicts does not meet anything in the database
+ then show the relation but do not recurse */
+ if (Hit == false && D.IsNegative() == true)
+ {
+ if (Show[D.TargetPkg()->ID] == None &&
+ Show[D.TargetPkg()->ID] != ToShow)
+ Show[D.TargetPkg()->ID] = ToShowNR;
+ }
+ else
+ {
+ if (GivenOnly == true && Show[D.TargetPkg()->ID] != ToShow)
+ Show[D.TargetPkg()->ID] = ToShowNR;
+ else
+ Show[D.TargetPkg()->ID] = ToShow;
+ }
+ }
+
+ // Edge colour
+ switch(D->Type)
+ {
+ case pkgCache::Dep::Conflicts:
+ case pkgCache::Dep::Obsoletes:
+ case pkgCache::Dep::DpkgBreaks:
+ printf("[color=springgreen];\n");
+ break;
+
+ case pkgCache::Dep::PreDepends:
+ printf("[color=blue];\n");
+ break;
+
+ default:
+ printf(";\n");
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Draw the box colours after the fact since we can not tell what colour
+ they should be until everything is finished drawing */
+ for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); Pkg.end() == false; ++Pkg)
+ {
+ if (Show[Pkg->ID] < DoneNR)
+ continue;
+
+ // Orange box for early recursion stoppage
+ if (Show[Pkg->ID] == DoneNR)
+ printf("\"%s\" [color=orange,shape=%s];\n",Pkg.FullName(true).c_str(),
+ Shapes[ShapeMap[Pkg->ID]]);
+ else
+ printf("\"%s\" [shape=%s];\n",Pkg.FullName(true).c_str(),
+ Shapes[ShapeMap[Pkg->ID]]);
+ }
+
+ printf("}\n");
+ delete[] Show;
+ delete[] Flags;
+ delete[] ShapeMap;
+ return true;
+}
+ /*}}}*/
+/* ShowAuto - show automatically installed packages (sorted) {{{*/
+static bool ShowAuto(CommandLine &)
+{
+ pkgCacheFile CacheFile;
+ pkgCache *Cache = CacheFile.GetPkgCache();
+ pkgDepCache *DepCache = CacheFile.GetDepCache();
+ if (unlikely(Cache == NULL || DepCache == NULL))
+ return false;
+
+ std::vector<string> packages;
+ packages.reserve(Cache->HeaderP->PackageCount / 3);
+
+ for (pkgCache::PkgIterator P = Cache->PkgBegin(); P.end() == false; ++P)
+ if ((*DepCache)[P].Flags & pkgCache::Flag::Auto)
+ packages.push_back(P.Name());
+
+ std::sort(packages.begin(), packages.end());
+
+ for (vector<string>::iterator I = packages.begin(); I != packages.end(); ++I)
+ cout << *I << "\n";
+
+ _error->Notice(_("This command is deprecated. Please use 'apt-mark showauto' instead."));
+ return true;
+}
+ /*}}}*/
+// ShowPkgNames - Show package names /*{{{*/
+// ---------------------------------------------------------------------
+/* This does a prefix match on the first argument */
+static bool ShowPkgNames(CommandLine &CmdL)
+{
+ pkgCacheFile CacheFile;
+ if (unlikely(CacheFile.BuildCaches(NULL, false) == false))
+ return false;
+ pkgCache::GrpIterator I = CacheFile.GetPkgCache()->GrpBegin();
+ bool const All = _config->FindB("APT::Cache::AllNames", false);
+
+ if (CmdL.FileList[1] != 0)
+ {
+ for (;I.end() != true; ++I)
+ {
+ if (All == false && (I.PackageList().end() || I.PackageList()->VersionList == 0))
+ continue;
+ if (strncmp(I.Name(),CmdL.FileList[1],strlen(CmdL.FileList[1])) == 0)
+ cout << I.Name() << endl;
+ }
+
+ return true;
+ }
+
+ // Show all pkgs
+ for (;I.end() != true; ++I)
+ {
+ if (All == false && (I.PackageList().end() || I.PackageList()->VersionList == 0))
+ continue;
+ cout << I.Name() << endl;
+ }
+
+ return true;
+}
+ /*}}}*/
+// Madison - Look a bit like katie's madison /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+static bool Madison(CommandLine &CmdL)
+{
+ pkgCacheFile CacheFile;
+ pkgSourceList *SrcList = CacheFile.GetSourceList();
+
+ if (SrcList == 0)
+ return false;
+
+ // Create the src text record parsers and ignore errors about missing
+ // deb-src lines that are generated from pkgSrcRecords::pkgSrcRecords
+ pkgSrcRecords SrcRecs(*SrcList);
+ if (_error->PendingError() == true)
+ _error->Discard();
+
+ APT::CacheSetHelper helper(true, GlobalError::NOTICE);
+ for (const char **I = CmdL.FileList + 1; *I != 0; I++)
+ {
+ _error->PushToStack();
+ APT::PackageList pkgset = APT::PackageList::FromString(CacheFile, *I, helper);
+ for (APT::PackageList::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg)
+ {
+ for (pkgCache::VerIterator V = Pkg.VersionList(); V.end() == false; ++V)
+ {
+ for (pkgCache::VerFileIterator VF = V.FileList(); VF.end() == false; ++VF)
+ {
+// This might be nice, but wouldn't uniquely identify the source -mdz
+// if (VF.File().Archive() != 0)
+// {
+// cout << setw(10) << Pkg.Name() << " | " << setw(10) << V.VerStr() << " | "
+// << VF.File().Archive() << endl;
+// }
+
+ // Locate the associated index files so we can derive a description
+ for (pkgSourceList::const_iterator S = SrcList->begin(); S != SrcList->end(); ++S)
+ {
+ vector<pkgIndexFile *> *Indexes = (*S)->GetIndexFiles();
+ for (vector<pkgIndexFile *>::const_iterator IF = Indexes->begin();
+ IF != Indexes->end(); ++IF)
+ {
+ if ((*IF)->FindInCache(*(VF.File().Cache())) == VF.File())
+ {
+ cout << setw(10) << Pkg.FullName(true) << " | " << setw(10) << V.VerStr() << " | "
+ << (*IF)->Describe(true) << endl;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ SrcRecs.Restart();
+ pkgSrcRecords::Parser *SrcParser;
+ bool foundSomething = false;
+ while ((SrcParser = SrcRecs.Find(*I, false)) != 0)
+ {
+ foundSomething = true;
+ // Maybe support Release info here too eventually
+ cout << setw(10) << SrcParser->Package() << " | "
+ << setw(10) << SrcParser->Version() << " | "
+ << SrcParser->Index().Describe(true) << endl;
+ }
+ if (foundSomething == true)
+ _error->RevertToStack();
+ else
+ _error->MergeWithStack();
+ }
+
+ return true;
+}
+ /*}}}*/
+// GenCaches - Call the main cache generator /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+static bool GenCaches(CommandLine &)
+{
+ OpTextProgress Progress(*_config);
+
+ pkgCacheFile CacheFile;
+ return CacheFile.BuildCaches(&Progress, true);
+}
+ /*}}}*/
+static bool ShowHelp(CommandLine &) /*{{{*/
+{
+ std::cout <<
+ _("Usage: apt-cache [options] command\n"
+ " apt-cache [options] show pkg1 [pkg2 ...]\n"
+ "\n"
+ "apt-cache queries and displays available information about installed\n"
+ "and installable packages. It works exclusively on the data acquired\n"
+ "into the local cache via the 'update' command of e.g. apt-get. The\n"
+ "displayed information may therefore be outdated if the last update was\n"
+ "too long ago, but in exchange apt-cache works independently of the\n"
+ "availability of the configured sources (e.g. offline).\n");
+ return true;
+}
+ /*}}}*/
+static std::vector<aptDispatchWithHelp> GetCommands() /*{{{*/
+{
+ return {
+ {"gencaches",&GenCaches, nullptr},
+ {"showsrc",&ShowSrcPackage, _("Show source records")},
+ {"showpkg",&DumpPackage, nullptr},
+ {"stats",&Stats, nullptr},
+ {"dump",&Dump, nullptr},
+ {"dumpavail",&DumpAvail, nullptr},
+ {"unmet",&UnMet, nullptr},
+ {"search",&DoSearch, _("Search the package list for a regex pattern")},
+ {"depends",&Depends, _("Show raw dependency information for a package")},
+ {"rdepends",&RDepends, _("Show reverse dependency information for a package")},
+ {"dotty",&Dotty, nullptr},
+ {"xvcg",&XVcg, nullptr},
+ {"show",&ShowPackage, _("Show a readable record for the package")},
+ {"pkgnames",&ShowPkgNames, _("List the names of all packages in the system")},
+ {"showauto",&ShowAuto, nullptr},
+ {"policy",&Policy, _("Show policy settings")},
+ {"madison",&Madison, nullptr},
+ {nullptr, nullptr, nullptr}
+ };
+}
+ /*}}}*/
+int main(int argc,const char *argv[]) /*{{{*/
+{
+ // Parse the command line and initialize the package library
+ CommandLine CmdL;
+ auto const Cmds = ParseCommandLine(CmdL, APT_CMD::APT_CACHE, &_config, &_system, argc, argv, &ShowHelp, &GetCommands);
+
+ InitOutput();
+
+ if (_config->Exists("APT::Cache::Generate") == true)
+ _config->Set("pkgCacheFile::Generate", _config->FindB("APT::Cache::Generate", true));
+
+ return DispatchCommandLine(CmdL, Cmds);
+}
+ /*}}}*/
diff --git a/cmdline/apt-cdrom.cc b/cmdline/apt-cdrom.cc
new file mode 100644
index 0000000..94202d9
--- /dev/null
+++ b/cmdline/apt-cdrom.cc
@@ -0,0 +1,235 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ APT CDROM - Tool for handling APT's CDROM database.
+
+ Currently the only option is 'add' which will take the current CD
+ in the drive and add it into the database.
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/cdrom.h>
+#include <apt-pkg/cdromutl.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/init.h>
+#include <apt-pkg/pkgsystem.h>
+#include <apt-pkg/progress.h>
+#include <apt-pkg/strutl.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <apt-private/private-cmndline.h>
+#include <apt-private/private-main.h>
+#include <apt-private/private-output.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+using namespace std;
+
+class pkgCdromTextStatus : public pkgCdromStatus /*{{{*/
+{
+protected:
+ OpTextProgress Progress;
+ void Prompt(const char *Text);
+ string PromptLine(const char *Text);
+ bool AskCdromName(string &name) APT_OVERRIDE;
+
+public:
+ virtual void Update(string text, int current) APT_OVERRIDE;
+ virtual bool ChangeCdrom() APT_OVERRIDE;
+ virtual OpProgress* GetOpProgress() APT_OVERRIDE;
+};
+
+void pkgCdromTextStatus::Prompt(const char *Text)
+{
+ char C;
+ cout << Text << ' ' << flush;
+ if (read(STDIN_FILENO,&C,1) < 0)
+ _error->Errno("pkgCdromTextStatus::Prompt",
+ "Failed to read from standard input (not a terminal?)");
+ if (C != '\n')
+ cout << endl;
+}
+
+string pkgCdromTextStatus::PromptLine(const char *Text)
+{
+ cout << Text << ':' << endl;
+
+ string Res;
+ getline(cin,Res);
+ return Res;
+}
+
+bool pkgCdromTextStatus::AskCdromName(string &name)
+{
+ cout << _("Please provide a name for this Disc, such as 'Debian 5.0.3 Disk 1'") << flush;
+ name = PromptLine("");
+
+ return true;
+}
+
+
+void pkgCdromTextStatus::Update(string text, int /*current*/)
+{
+ if(text.size() > 0)
+ cout << text << flush;
+}
+
+bool pkgCdromTextStatus::ChangeCdrom()
+{
+ Prompt(_("Please insert a Disc in the drive and press [Enter]"));
+ return true;
+}
+
+APT_PURE OpProgress* pkgCdromTextStatus::GetOpProgress()
+{
+ return &Progress;
+}
+ /*}}}*/
+// AddOrIdent - Add or Ident a CDROM /*{{{*/
+static bool AddOrIdent(bool const Add)
+{
+ pkgUdevCdromDevices UdevCdroms;
+ pkgCdromTextStatus log;
+ pkgCdrom cdrom;
+
+ bool oneSuccessful = false;
+ bool AutoDetect = _config->FindB("Acquire::cdrom::AutoDetect", true);
+ if (AutoDetect == true)
+ {
+ bool const Debug = _config->FindB("Debug::Acquire::cdrom", false);
+ std::string const CDMount = _config->Find("Acquire::cdrom::mount");
+ bool const NoMount = _config->FindB("APT::CDROM::NoMount", false);
+ if (NoMount == false)
+ _config->Set("APT::CDROM::NoMount", true);
+
+ vector<struct CdromDevice> const v = UdevCdroms.Scan();
+ for (std::vector<struct CdromDevice>::const_iterator cd = v.begin(); cd != v.end(); ++cd)
+ {
+ if (Debug)
+ clog << "Looking at device:"
+ << "\tDeviveName: '" << cd->DeviceName << "'"
+ << "\tIsMounted: '" << cd->Mounted << "'"
+ << "\tMountPoint: '" << cd->MountPath << "'"
+ << endl;
+
+ std::string AptMountPoint;
+ if (cd->Mounted)
+ _config->Set("Acquire::cdrom::mount", cd->MountPath);
+ else if (NoMount == true)
+ continue;
+ else
+ {
+ AptMountPoint = _config->FindDir("Dir::Media::MountPath");
+ if (FileExists(AptMountPoint) == false)
+ mkdir(AptMountPoint.c_str(), 0750);
+ if(MountCdrom(AptMountPoint, cd->DeviceName) == false)
+ {
+ _error->Warning(_("Failed to mount '%s' to '%s'"), cd->DeviceName.c_str(), AptMountPoint.c_str());
+ continue;
+ }
+ _config->Set("Acquire::cdrom::mount", AptMountPoint);
+ }
+
+ _error->PushToStack();
+ if (Add == true)
+ oneSuccessful = cdrom.Add(&log);
+ else
+ {
+ std::string id;
+ oneSuccessful = cdrom.Ident(id, &log);
+ }
+ _error->MergeWithStack();
+
+ if (AptMountPoint.empty() == false)
+ UnmountCdrom(AptMountPoint);
+ }
+ if (NoMount == false)
+ _config->Set("APT::CDROM::NoMount", NoMount);
+ _config->Set("Acquire::cdrom::mount", CDMount);
+ }
+
+ // fallback if auto-detect didn't work
+ if (oneSuccessful == false)
+ {
+ _error->PushToStack();
+ if (Add == true)
+ oneSuccessful = cdrom.Add(&log);
+ else
+ {
+ std::string id;
+ oneSuccessful = cdrom.Ident(id, &log);
+ }
+ _error->MergeWithStack();
+ }
+
+ if (oneSuccessful == false)
+ _error->Error("%s", _("No CD-ROM could be auto-detected or found using the default mount point.\n"
+ "You may try the --cdrom option to set the CD-ROM mount point.\n"
+ "See 'man apt-cdrom' for more information about the CD-ROM auto-detection and mount point."));
+ else if (Add == true)
+ cout << _("Repeat this process for the rest of the CDs in your set.") << endl;
+
+ return oneSuccessful;
+}
+ /*}}}*/
+// DoAdd - Add a new CDROM /*{{{*/
+// ---------------------------------------------------------------------
+/* This does the main add bit.. We show some status and things. The
+ sequence is to mount/umount the CD, Ident it then scan it for package
+ files and reduce that list. Then we copy over the package files and
+ verify them. Then rewrite the database files */
+static bool DoAdd(CommandLine &)
+{
+ return AddOrIdent(true);
+}
+ /*}}}*/
+// DoIdent - Ident a CDROM /*{{{*/
+static bool DoIdent(CommandLine &)
+{
+ return AddOrIdent(false);
+}
+ /*}}}*/
+static bool ShowHelp(CommandLine &) /*{{{*/
+{
+ std::cout <<
+ _("Usage: apt-cdrom [options] command\n"
+ "\n"
+ "apt-cdrom is used to add CDROM's, USB flashdrives and other removable\n"
+ "media types as package sources to APT. The mount point and device\n"
+ "information is taken from apt.conf(5), udev(7) and fstab(5).\n");
+ return true;
+}
+ /*}}}*/
+static std::vector<aptDispatchWithHelp> GetCommands() /*{{{*/
+{
+ return {
+ {"add", &DoAdd, "Add a CDROM"},
+ {"ident", &DoIdent, "Report the identity of a CDROM"},
+ {nullptr, nullptr, nullptr}
+ };
+}
+ /*}}}*/
+int main(int argc,const char *argv[]) /*{{{*/
+{
+ // Parse the command line and initialize the package library
+ CommandLine CmdL;
+ auto const Cmds = ParseCommandLine(CmdL, APT_CMD::APT_CDROM, &_config, &_system, argc, argv, &ShowHelp, &GetCommands);
+
+ InitOutput();
+
+ return DispatchCommandLine(CmdL, Cmds);
+}
+ /*}}}*/
diff --git a/cmdline/apt-config.cc b/cmdline/apt-config.cc
new file mode 100644
index 0000000..8ad7cac
--- /dev/null
+++ b/cmdline/apt-config.cc
@@ -0,0 +1,144 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ APT Config - Program to manipulate APT configuration files
+
+ This program will parse a config file and then do something with it.
+
+ Commands:
+ shell - Shell mode. After this a series of word pairs should occur.
+ The first is the environment var to set and the second is
+ the key to set it from. Use like:
+ eval `apt-config shell QMode apt::QMode`
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/aptconfiguration.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/init.h>
+#include <apt-pkg/pkgsystem.h>
+#include <apt-pkg/strutl.h>
+
+#include <iostream>
+#include <map>
+#include <string>
+#include <vector>
+#include <string.h>
+
+#include <apt-private/private-cmndline.h>
+#include <apt-private/private-main.h>
+
+#include <apti18n.h>
+ /*}}}*/
+using namespace std;
+
+// DoShell - Handle the shell command /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+static bool DoShell(CommandLine &CmdL)
+{
+ for (const char **I = CmdL.FileList + 1; *I != 0; I += 2)
+ {
+ if (I[1] == 0 || strlen(I[1]) == 0)
+ return _error->Error(_("Arguments not in pairs"));
+
+ string key = I[1];
+ if (key.end()[-1] == '/') // old directory format
+ key.append("d");
+
+ if (_config->ExistsAny(key.c_str()))
+ cout << *I << "='" <<
+ SubstVar(_config->FindAny(key.c_str()),"'","'\\''") << '\'' << endl;
+
+ }
+
+ return true;
+}
+ /*}}}*/
+// DoDump - Dump the configuration space /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+static bool DoDump(CommandLine &CmdL)
+{
+ bool const empty = _config->FindB("APT::Config::Dump::EmptyValue", true);
+ std::string const format = _config->Find("APT::Config::Dump::Format", "%F \"%v\";\n");
+ if (CmdL.FileSize() == 1)
+ _config->Dump(cout, NULL, format.c_str(), empty);
+ else
+ for (const char **I = CmdL.FileList + 1; *I != 0; ++I)
+ _config->Dump(cout, *I, format.c_str(), empty);
+ return true;
+}
+ /*}}}*/
+static bool ShowHelp(CommandLine &) /*{{{*/
+{
+ std::cout <<
+ _("Usage: apt-config [options] command\n"
+ "\n"
+ "apt-config is an interface to the configuration settings used by\n"
+ "all APT tools, mainly intended for debugging and shell scripting.\n");
+ return true;
+}
+ /*}}}*/
+static std::vector<aptDispatchWithHelp> GetCommands() /*{{{*/
+{
+ return {
+ {"shell", &DoShell, _("get configuration values via shell evaluation")},
+ {"dump", &DoDump, _("show the active configuration setting")},
+ {nullptr, nullptr, nullptr}
+ };
+}
+ /*}}}*/
+int main(int argc,const char *argv[]) /*{{{*/
+{
+ // Parse the command line and initialize the package library
+ CommandLine CmdL;
+ auto const Cmds = ParseCommandLine(CmdL, APT_CMD::APT_CONFIG, &_config, &_system, argc, argv, &ShowHelp, &GetCommands);
+
+ std::vector<std::string> const langs = APT::Configuration::getLanguages(true);
+ _config->Clear("Acquire::Languages");
+ for (std::vector<std::string>::const_iterator l = langs.begin(); l != langs.end(); ++l)
+ _config->Set("Acquire::Languages::", *l);
+
+ std::vector<std::string> const archs = APT::Configuration::getArchitectures();
+ _config->Clear("APT::Architectures");
+ for (std::vector<std::string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
+ _config->Set("APT::Architectures::", *a);
+
+ string const conf = "APT::Compressor::";
+ std::map<std::string,std::string> CompressorNames;
+ for (auto && key : _config->FindVector("APT::Compressor", "", true))
+ {
+ auto const comp = conf + key + "::Name";
+ CompressorNames.emplace(_config->Find(comp, key), key);
+ }
+ std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
+ _config->Clear("APT::Compressor");
+ for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressors.begin(); c != compressors.end(); ++c)
+ {
+ auto const n = CompressorNames.find(c->Name);
+ string comp = conf + (n == CompressorNames.end() ? c->Name : n->second) + "::";
+ _config->Set(comp + "Name", c->Name);
+ _config->Set(comp + "Extension", c->Extension);
+ _config->Set(comp + "Binary", c->Binary);
+ _config->Set((comp + "Cost").c_str(), c->Cost);
+ for (std::vector<std::string>::const_iterator a = c->CompressArgs.begin(); a != c->CompressArgs.end(); ++a)
+ _config->Set(comp + "CompressArg::", *a);
+ for (std::vector<std::string>::const_iterator a = c->UncompressArgs.begin(); a != c->UncompressArgs.end(); ++a)
+ _config->Set(comp + "UncompressArg::", *a);
+ }
+
+ std::vector<std::string> const profiles = APT::Configuration::getBuildProfiles();
+ _config->Clear("APT::Build-Profiles");
+ for (std::vector<std::string>::const_iterator p = profiles.begin(); p != profiles.end(); ++p)
+ _config->Set("APT::Build-Profiles::", *p);
+
+ return DispatchCommandLine(CmdL, Cmds);
+}
+ /*}}}*/
diff --git a/cmdline/apt-dump-solver.cc b/cmdline/apt-dump-solver.cc
new file mode 100644
index 0000000..ab44b9f
--- /dev/null
+++ b/cmdline/apt-dump-solver.cc
@@ -0,0 +1,184 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* #####################################################################
+
+ dummy solver to get quickly a scenario file out of APT
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/edsp.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/strutl.h>
+
+#include <apt-private/private-cmndline.h>
+
+#include <cstdio>
+#include <iostream>
+#include <memory>
+#include <sstream>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <string.h>
+#include <unistd.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+static bool ShowHelp(CommandLine &) /*{{{*/
+{
+ std::cout <<
+ _("Usage: apt-dump-solver\n"
+ "\n"
+ "apt-dump-solver is an interface to store an EDSP scenario in\n"
+ "a file and optionally forwards it to another solver.\n");
+ return true;
+}
+ /*}}}*/
+static std::vector<aptDispatchWithHelp> GetCommands() /*{{{*/
+{
+ return {};
+}
+ /*}}}*/
+static int WriteError(char const * const uid, std::ostringstream &out, FileFd &stdoutfd, pid_t const &Solver)/*{{{*/
+{
+ _error->DumpErrors(out);
+ // ensure the solver isn't printing into "our" error message, too
+ if (Solver != 0)
+ ExecWait(Solver, "dump", true);
+ EDSP::WriteError(uid, out.str(), stdoutfd);
+ return 0;
+}
+ /*}}}*/
+int main(int argc,const char *argv[]) /*{{{*/
+{
+ CommandLine CmdL;
+ ParseCommandLine(CmdL, APT_CMD::APT_DUMP_SOLVER, &_config, nullptr, argc, argv, &ShowHelp, &GetCommands);
+ _config->Clear("Dir::Log");
+
+ bool const is_forwarding_dumper = (CmdL.FileSize() != 0);
+
+ FileFd stdoutfd;
+ if (stdoutfd.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false)
+ return 252;
+
+ FileFd dump;
+ char const * const filename = is_forwarding_dumper ? CmdL.FileList[0] : getenv("APT_EDSP_DUMP_FILENAME");
+ if (filename == nullptr || strlen(filename) == 0)
+ {
+ if (is_forwarding_dumper == false)
+ {
+ EDSP::WriteError("ERR_NO_FILENAME", "You have to set the environment variable APT_EDSP_DUMP_FILENAME\n"
+ "to a valid filename to store the dump of EDSP solver input in.\n"
+ "For example with: export APT_EDSP_DUMP_FILENAME=/tmp/dump.edsp", stdoutfd);
+ return 0;
+ }
+ }
+ else
+ {
+ // ignore errors here as logging isn't really critical
+ _error->PushToStack();
+ if (dump.Open(filename, FileFd::WriteOnly | FileFd::Exclusive | FileFd::Create, FileFd::Extension, 0644) == false &&
+ is_forwarding_dumper == false)
+ {
+ _error->MergeWithStack();
+ std::ostringstream out;
+ out << "Writing EDSP solver input to file '" << filename << "' failed as it couldn't be created!\n";
+ return WriteError("ERR_CREATE_FILE", out, stdoutfd, 0);
+ }
+ _error->RevertToStack();
+ }
+
+ pid_t Solver = 0;
+ FileFd forward;
+ if (is_forwarding_dumper)
+ {
+ signal(SIGPIPE, SIG_IGN);
+ int external[] = {-1, -1};
+ if (pipe(external) != 0)
+ return 250;
+ for (int i = 0; i < 2; ++i)
+ SetCloseExec(external[i], true);
+
+ Solver = ExecFork();
+ if (Solver == 0) {
+ _config->Set("APT::Sandbox::User", _config->Find("APT::Solver::RunAsUser", _config->Find("APT::Sandbox::User")));
+ DropPrivileges();
+ dup2(external[0], STDIN_FILENO);
+ execv(CmdL.FileList[1], const_cast<char**>(CmdL.FileList + 1));
+ std::cerr << "Failed to execute '" << CmdL.FileList[1] << "'!" << std::endl;
+ _exit(100);
+ }
+ close(external[0]);
+
+ if (WaitFd(external[1], true, 5) == false)
+ return 251;
+
+ if (forward.OpenDescriptor(external[1], FileFd::WriteOnly | FileFd::BufferedWrite, true) == false)
+ return 252;
+ }
+
+ DropPrivileges();
+
+ FileFd input;
+ if (input.OpenDescriptor(STDIN_FILENO, FileFd::ReadOnly) == false)
+ {
+ std::ostringstream out;
+ out << "Writing EDSP solver input to file '" << filename << "' failed as stdin couldn't be opened!\n";
+ return WriteError("ERR_READ_ERROR", out, stdoutfd, Solver);
+ }
+
+ std::unique_ptr<char[]> Buf(new char[APT_BUFFER_SIZE]);
+ unsigned long long ToRead = 0;
+ do {
+ if (input.Read(Buf.get(), APT_BUFFER_SIZE, &ToRead) == false)
+ {
+ std::ostringstream out;
+ out << "Writing EDSP solver input to file '" << filename << "' failed as reading from stdin failed!\n";
+ return WriteError("ERR_READ_ERROR", out, stdoutfd, Solver);
+ }
+ if (ToRead == 0)
+ break;
+ if (forward.IsOpen() && forward.Failed() == false && forward.Write(Buf.get(),ToRead) == false)
+ forward.Close();
+ if (dump.IsOpen() && dump.Failed() == false && dump.Write(Buf.get(),ToRead) == false)
+ dump.Close();
+ } while (true);
+ input.Close();
+ forward.Close();
+ dump.Close();
+
+ if (is_forwarding_dumper)
+ {
+ // Wait and collect the error code
+ int Status;
+ while (waitpid(Solver, &Status, 0) != Solver)
+ {
+ if (errno == EINTR)
+ continue;
+
+ std::ostringstream out;
+ ioprintf(out, _("Waited for %s but it wasn't there"), CmdL.FileList[1]);
+ return WriteError("ERR_FORWARD", out, stdoutfd, 0);
+ }
+ if (WIFEXITED(Status))
+ return WEXITSTATUS(Status);
+ else
+ return 255;
+ }
+ else if (_error->PendingError())
+ {
+ std::ostringstream out;
+ out << "Writing EDSP solver input to file '" << filename << "' failed due to write errors!\n";
+ return WriteError("ERR_WRITE_ERROR", out, stdoutfd, Solver);
+ }
+ else
+ EDSP::WriteError("ERR_JUST_DUMPING", "I am too dumb, i can just dump!\nPlease use one of my friends instead!", stdoutfd);
+ return 0;
+}
diff --git a/cmdline/apt-extracttemplates.cc b/cmdline/apt-extracttemplates.cc
new file mode 100644
index 0000000..88a2041
--- /dev/null
+++ b/cmdline/apt-extracttemplates.cc
@@ -0,0 +1,336 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ APT Extract Templates - Program to extract debconf config and template
+ files
+
+ This is a simple program to extract config and template information
+ from Debian packages. It can be used to speed up the preconfiguration
+ process for debconf-enabled packages
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/debfile.h>
+#include <apt-pkg/deblistparser.h>
+#include <apt-pkg/dirstream.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/init.h>
+#include <apt-pkg/mmap.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/pkgcachegen.h>
+#include <apt-pkg/pkgsystem.h>
+#include <apt-pkg/sourcelist.h>
+#include <apt-pkg/strutl.h>
+#include <apt-pkg/tagfile-keys.h>
+#include <apt-pkg/tagfile.h>
+#include <apt-pkg/version.h>
+
+#include <apt-private/private-cmndline.h>
+#include <apt-private/private-main.h>
+
+#include <iostream>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "apt-extracttemplates.h"
+
+#include <apti18n.h>
+ /*}}}*/
+
+using namespace std;
+
+pkgCache *DebFile::Cache = 0;
+
+// DebFile::DebFile - Construct the DebFile object /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+DebFile::DebFile(const char *debfile)
+ : File(debfile, FileFd::ReadOnly), Control(NULL), ControlLen(0),
+ DepOp(0), PreDepOp(0), Config(0), Template(0), Which(None)
+{
+}
+ /*}}}*/
+// DebFile::~DebFile - Destruct the DebFile object /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+DebFile::~DebFile()
+{
+ delete [] Control;
+ delete [] Config;
+ delete [] Template;
+}
+ /*}}}*/
+// DebFile::GetInstalledVer - Find out the installed version of a pkg /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+string DebFile::GetInstalledVer(const string &package)
+{
+ pkgCache::PkgIterator Pkg = Cache->FindPkg(package);
+ if (Pkg.end() == false)
+ {
+ pkgCache::VerIterator V = Pkg.CurrentVer();
+ if (V.end() == false)
+ {
+ return V.VerStr();
+ }
+ }
+
+ return string();
+}
+ /*}}}*/
+// DebFile::Go - Start extracting a debian package /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool DebFile::Go()
+{
+ debDebFile Deb(File);
+
+ return Deb.ExtractTarMember(*this, "control.tar");
+}
+ /*}}}*/
+// DebFile::DoItem examine element in package and mark /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool DebFile::DoItem(Item &I, int &Fd)
+{
+ if (strcmp(I.Name, "control") == 0)
+ {
+ delete [] Control;
+ Control = new char[I.Size+3];
+ Control[I.Size] = '\n';
+ Control[I.Size + 1] = '\n';
+ Control[I.Size + 2] = '\0';
+ Which = IsControl;
+ ControlLen = I.Size + 3;
+ // make it call the Process method below. this is so evil
+ Fd = -2;
+ }
+ else if (strcmp(I.Name, "config") == 0)
+ {
+ delete [] Config;
+ Config = new char[I.Size+1];
+ Config[I.Size] = 0;
+ Which = IsConfig;
+ Fd = -2;
+ }
+ else if (strcmp(I.Name, "templates") == 0)
+ {
+ delete [] Template;
+ Template = new char[I.Size+1];
+ Template[I.Size] = 0;
+ Which = IsTemplate;
+ Fd = -2;
+ }
+ else
+ {
+ // Ignore it
+ Fd = -1;
+ }
+ return true;
+}
+ /*}}}*/
+// DebFile::Process examine element in package and copy /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool DebFile::Process(Item &/*I*/, const unsigned char *data,
+ unsigned long long size, unsigned long long pos)
+{
+ switch (Which)
+ {
+ case IsControl:
+ memcpy(Control + pos, data, size);
+ break;
+ case IsConfig:
+ memcpy(Config + pos, data, size);
+ break;
+ case IsTemplate:
+ memcpy(Template + pos, data, size);
+ break;
+ default: /* throw it away */ ;
+ }
+ return true;
+}
+ /*}}}*/
+// DebFile::ParseInfo - Parse control file for dependency info /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool DebFile::ParseInfo()
+{
+ if (Control == NULL) return false;
+
+ pkgTagSection Section;
+ if (Section.Scan(Control, ControlLen) == false)
+ return false;
+
+ Package = Section.Find(pkgTagSection::Key::Package).to_string();
+ Version = GetInstalledVer(Package);
+
+ const char *Start, *Stop;
+ if (Section.Find(pkgTagSection::Key::Depends, Start, Stop))
+ {
+ while (1)
+ {
+ string P, V;
+ unsigned int Op;
+ Start = debListParser::ParseDepends(Start, Stop, P, V, Op);
+ if (Start == 0) return false;
+ if (P == "debconf")
+ {
+ DepVer = V;
+ DepOp = Op;
+ break;
+ }
+ if (Start == Stop) break;
+ }
+ }
+
+ if (Section.Find(pkgTagSection::Key::Pre_Depends, Start, Stop))
+ {
+ while (1)
+ {
+ string P, V;
+ unsigned int Op;
+ Start = debListParser::ParseDepends(Start, Stop, P, V, Op);
+ if (Start == 0) return false;
+ if (P == "debconf")
+ {
+ PreDepVer = V;
+ PreDepOp = Op;
+ break;
+ }
+ if (Start == Stop) break;
+ }
+ }
+
+ return true;
+}
+ /*}}}*/
+static bool ShowHelp(CommandLine &) /*{{{*/
+{
+ cout <<
+ _("Usage: apt-extracttemplates file1 [file2 ...]\n"
+ "\n"
+ "apt-extracttemplates is used to extract config and template files\n"
+ "from debian packages. It is used mainly by debconf(1) to prompt for\n"
+ "configuration questions before installation of packages.\n");
+ return true;
+}
+ /*}}}*/
+// WriteFile - write the contents of the passed string to a file /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+static string WriteFile(const char *package, const char *prefix, const char *data)
+{
+ FileFd f;
+ std::string tplname;
+ strprintf(tplname, "%s.%s", package, prefix);
+ GetTempFile(tplname, false, &f);
+ if (data != nullptr)
+ f.Write(data, strlen(data));
+ return f.Name();
+}
+ /*}}}*/
+// WriteConfig - write out the config data from a debian package file /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+static void WriteConfig(const DebFile &file)
+{
+ string templatefile = WriteFile(file.Package.c_str(), "template", file.Template);
+ string configscript = WriteFile(file.Package.c_str(), "config", file.Config);
+
+ if (templatefile.empty() == true || configscript.empty() == true)
+ return;
+ cout << file.Package << " " << file.Version << " "
+ << templatefile << " " << configscript << endl;
+}
+ /*}}}*/
+// InitCache - initialize the package cache /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+static bool Go(CommandLine &CmdL)
+{
+ // Initialize the apt cache
+ MMap *Map = 0;
+ pkgSourceList List;
+ List.ReadMainList();
+ pkgCacheGenerator::MakeStatusCache(List,NULL,&Map,true);
+ if (Map == 0)
+ return false;
+ DebFile::Cache = new pkgCache(Map);
+ if (_error->PendingError() == true)
+ return false;
+
+ // Find out what version of debconf is currently installed
+ string debconfver = DebFile::GetInstalledVer("debconf");
+ if (debconfver.empty() == true)
+ return _error->Error( _("Cannot get debconf version. Is debconf installed?"));
+
+ auto const tmpdir = _config->Find("APT::ExtractTemplates::TempDir");
+ if (tmpdir.empty() == false)
+ setenv("TMPDIR", tmpdir.c_str(), 1);
+
+ // Process each package passsed in
+ for (unsigned int I = 0; I != CmdL.FileSize(); I++)
+ {
+ // Will pick up the errors later..
+ DebFile file(CmdL.FileList[I]);
+ if (file.Go() == false)
+ {
+ _error->Error("Prior errors apply to %s",CmdL.FileList[I]);
+ continue;
+ }
+
+ // Does the package have templates?
+ if (file.Template != 0 && file.ParseInfo() == true)
+ {
+ // Check to make sure debconf dependencies are
+ // satisfied
+ // cout << "Check " << file.DepVer << ',' << debconfver << endl;
+ if (file.DepVer != "" &&
+ DebFile::Cache->VS->CheckDep(debconfver.c_str(),
+ file.DepOp,file.DepVer.c_str()
+ ) == false)
+ continue;
+ if (file.PreDepVer != "" &&
+ DebFile::Cache->VS->CheckDep(debconfver.c_str(),
+ file.PreDepOp,file.PreDepVer.c_str()
+ ) == false)
+ continue;
+
+ WriteConfig(file);
+ }
+ }
+
+
+ delete Map;
+ delete DebFile::Cache;
+
+ return !_error->PendingError();
+}
+ /*}}}*/
+static std::vector<aptDispatchWithHelp> GetCommands() /*{{{*/
+{
+ return {
+ {nullptr, nullptr, nullptr}
+ };
+}
+ /*}}}*/
+int main(int argc, const char **argv) /*{{{*/
+{
+ CommandLine CmdL;
+ ParseCommandLine(CmdL, APT_CMD::APT_EXTRACTTEMPLATES, &_config, &_system, argc, argv, &ShowHelp, &GetCommands);
+
+ Go(CmdL);
+
+ return DispatchCommandLine(CmdL, {});
+}
+ /*}}}*/
diff --git a/cmdline/apt-extracttemplates.h b/cmdline/apt-extracttemplates.h
new file mode 100644
index 0000000..bbd926e
--- /dev/null
+++ b/cmdline/apt-extracttemplates.h
@@ -0,0 +1,49 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ apt-extracttemplate - tool to extract template and config data
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef _APTEXTRACTTEMPLATE_H_
+#define _APTEXTRACTTEMPLATE_H_
+
+#include <apt-pkg/dirstream.h>
+#include <apt-pkg/fileutl.h>
+
+#include <string>
+
+class pkgCache;
+
+class DebFile : public pkgDirStream
+{
+ FileFd File;
+ char *Control;
+ unsigned long ControlLen;
+
+public:
+ explicit DebFile(const char *FileName);
+ ~DebFile();
+ bool DoItem(Item &I, int &fd) APT_OVERRIDE;
+ bool Process(pkgDirStream::Item &I, const unsigned char *data,
+ unsigned long long size, unsigned long long pos) APT_OVERRIDE;
+
+ bool Go();
+ bool ParseInfo();
+
+ static std::string GetInstalledVer(const std::string &package);
+
+ std::string Package;
+ std::string Version;
+ std::string DepVer, PreDepVer;
+ unsigned int DepOp, PreDepOp;
+
+ char *Config;
+ char *Template;
+
+ static pkgCache *Cache;
+ enum { None, IsControl, IsConfig, IsTemplate } Which;
+};
+
+#endif
diff --git a/cmdline/apt-get.cc b/cmdline/apt-get.cc
new file mode 100644
index 0000000..95c4a85
--- /dev/null
+++ b/cmdline/apt-get.cc
@@ -0,0 +1,450 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ apt-get - Cover for dpkg
+
+ This is an allout cover for dpkg implementing a safer front end. It is
+ based largely on libapt-pkg.
+
+ The syntax is different,
+ apt-get [opt] command [things]
+ Where command is:
+ update - Resyncronize the package files from their sources
+ upgrade - Smart-Download the newest versions of all packages
+ dselect-upgrade - Follows dselect's changes to the Status: field
+ and installs new and removes old packages
+ dist-upgrade - Powerful upgrader designed to handle the issues with
+ a new distribution.
+ install - Download and install a given package (by name, not by .deb)
+ check - Update the package cache and check for broken packages
+ clean - Erase the .debs downloaded to /var/cache/apt/archives and
+ the partial dir too
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/acquire-item.h>
+#include <apt-pkg/acquire.h>
+#include <apt-pkg/algorithms.h>
+#include <apt-pkg/aptconfiguration.h>
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/cacheset.h>
+#include <apt-pkg/clean.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/debmetaindex.h>
+#include <apt-pkg/depcache.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/indexfile.h>
+#include <apt-pkg/init.h>
+#include <apt-pkg/macros.h>
+#include <apt-pkg/metaindex.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/pkgrecords.h>
+#include <apt-pkg/pkgsystem.h>
+#include <apt-pkg/progress.h>
+#include <apt-pkg/sourcelist.h>
+#include <apt-pkg/srcrecords.h>
+#include <apt-pkg/strutl.h>
+#include <apt-pkg/upgrade.h>
+#include <apt-pkg/version.h>
+
+#include <apt-private/acqprogress.h>
+#include <apt-private/private-cachefile.h>
+#include <apt-private/private-cacheset.h>
+#include <apt-private/private-cmndline.h>
+#include <apt-private/private-download.h>
+#include <apt-private/private-install.h>
+#include <apt-private/private-main.h>
+#include <apt-private/private-moo.h>
+#include <apt-private/private-output.h>
+#include <apt-private/private-source.h>
+#include <apt-private/private-update.h>
+#include <apt-private/private-upgrade.h>
+#include <apt-private/private-utils.h>
+
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <set>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <apti18n.h>
+ /*}}}*/
+
+using namespace std;
+
+/* mark packages as automatically/manually installed. {{{*/
+static bool DoMarkAuto(CommandLine &CmdL)
+{
+ bool Action = true;
+ int AutoMarkChanged = 0;
+ OpTextProgress progress;
+ CacheFile Cache;
+ if (Cache.Open() == false)
+ return false;
+
+ if (strcasecmp(CmdL.FileList[0],"markauto") == 0)
+ Action = true;
+ else if (strcasecmp(CmdL.FileList[0],"unmarkauto") == 0)
+ Action = false;
+
+ for (const char **I = CmdL.FileList + 1; *I != 0; I++)
+ {
+ const char *S = *I;
+ // Locate the package
+ pkgCache::PkgIterator Pkg = Cache->FindPkg(S);
+ if (Pkg.end() == true) {
+ return _error->Error(_("Couldn't find package %s"),S);
+ }
+ else
+ {
+ if (!Action)
+ ioprintf(c1out,_("%s set to manually installed.\n"), Pkg.Name());
+ else
+ ioprintf(c1out,_("%s set to automatically installed.\n"),
+ Pkg.Name());
+
+ Cache->MarkAuto(Pkg,Action);
+ AutoMarkChanged++;
+ }
+ }
+
+ _error->Notice(_("This command is deprecated. Please use 'apt-mark auto' and 'apt-mark manual' instead."));
+
+ if (AutoMarkChanged && ! _config->FindB("APT::Get::Simulate",false))
+ return Cache->writeStateFile(NULL);
+ return false;
+}
+ /*}}}*/
+// DoDSelectUpgrade - Do an upgrade by following dselects selections /*{{{*/
+// ---------------------------------------------------------------------
+/* Follows dselect's selections */
+static bool DoDSelectUpgrade(CommandLine &)
+{
+ CacheFile Cache;
+ if (Cache.OpenForInstall() == false || Cache.CheckDeps() == false)
+ return false;
+
+ pkgDepCache::ActionGroup group(Cache);
+
+ // Install everything with the install flag set
+ for (pkgCache::PkgIterator I = Cache->PkgBegin(); I.end() != true; ++I)
+ {
+ /* Install the package only if it is a new install, the autoupgrader
+ will deal with the rest */
+ if (I->SelectedState == pkgCache::State::Install)
+ Cache->MarkInstall(I,false);
+ }
+
+ /* Now install their deps too, if we do this above then order of
+ the status file is significant for | groups */
+ for (pkgCache::PkgIterator I = Cache->PkgBegin(); I.end() != true; ++I)
+ {
+ /* Install the package only if it is a new install, the autoupgrader
+ will deal with the rest */
+ if (I->SelectedState == pkgCache::State::Install)
+ Cache->MarkInstall(I,true);
+ }
+
+ // Apply erasures now, they override everything else.
+ for (pkgCache::PkgIterator I = Cache->PkgBegin(); I.end() != true; ++I)
+ {
+ // Remove packages
+ if (I->SelectedState == pkgCache::State::DeInstall ||
+ I->SelectedState == pkgCache::State::Purge)
+ Cache->MarkDelete(I,I->SelectedState == pkgCache::State::Purge);
+ }
+
+ /* Resolve any problems that dselect created, allupgrade cannot handle
+ such things. We do so quite aggressively too.. */
+ if (Cache->BrokenCount() != 0)
+ {
+ pkgProblemResolver Fix(Cache);
+
+ // Hold back held packages.
+ if (_config->FindB("APT::Ignore-Hold",false) == false)
+ {
+ for (pkgCache::PkgIterator I = Cache->PkgBegin(); I.end() == false; ++I)
+ {
+ if (I->SelectedState == pkgCache::State::Hold)
+ {
+ Fix.Protect(I);
+ Cache->MarkKeep(I);
+ }
+ }
+ }
+
+ if (Fix.Resolve() == false)
+ {
+ ShowBroken(c1out,Cache,false);
+ return _error->Error(_("Internal error, problem resolver broke stuff"));
+ }
+ }
+
+ // Now upgrade everything
+ if (APT::Upgrade::Upgrade(Cache, APT::Upgrade::FORBID_REMOVE_PACKAGES | APT::Upgrade::FORBID_INSTALL_NEW_PACKAGES) == false)
+ {
+ ShowBroken(c1out,Cache,false);
+ return _error->Error(_("Internal error, problem resolver broke stuff"));
+ }
+
+ APT::PackageVector HeldBackPackages;
+ for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); not Pkg.end(); ++Pkg)
+ if (Pkg->CurrentVer != 0 && Cache[Pkg].Upgradable() && not Cache[Pkg].Upgrade() && not Cache[Pkg].Delete())
+ HeldBackPackages.push_back(Pkg);
+
+ return InstallPackages(Cache, HeldBackPackages, false);
+}
+ /*}}}*/
+// DoCheck - Perform the check operation /*{{{*/
+// ---------------------------------------------------------------------
+/* Opening automatically checks the system, this command is mostly used
+ for debugging */
+static bool DoCheck(CommandLine &)
+{
+ CacheFile Cache;
+ Cache.Open();
+ Cache.CheckDeps();
+
+ return true;
+}
+ /*}}}*/
+// DoIndexTargets - Lists all IndexTargets /*{{{*/
+static std::string format_key(std::string key)
+{
+ // deb822 is case-insensitive, but the human eye prefers candy
+ std::transform(key.begin(), key.end(), key.begin(), ::tolower);
+ key[0] = ::toupper(key[0]);
+ size_t found = key.find("_uri");
+ if (found != std::string::npos)
+ key.replace(found, 4, "-URI");
+ while ((found = key.find('_')) != std::string::npos)
+ {
+ key[found] = '-';
+ key[found + 1] = ::toupper(key[found + 1]);
+ }
+ return key;
+}
+static bool DoIndexTargets(CommandLine &CmdL)
+{
+ pkgCacheFile CacheFile;
+ pkgSourceList *SrcList = CacheFile.GetSourceList();
+ pkgCache *Cache = CacheFile.GetPkgCache();
+
+ if (SrcList == nullptr || Cache == nullptr)
+ return false;
+
+ std::string const Format = _config->Find("APT::Get::IndexTargets::Format");
+ bool const ReleaseInfo = _config->FindB("APT::Get::IndexTargets::ReleaseInfo", true);
+ bool Filtered = CmdL.FileSize() > 1;
+ for (pkgSourceList::const_iterator S = SrcList->begin(); S != SrcList->end(); ++S)
+ {
+ std::vector<IndexTarget> const targets = (*S)->GetIndexTargets();
+ std::map<std::string, string> AddOptions;
+ if (ReleaseInfo)
+ {
+ AddOptions.insert(std::make_pair("TRUSTED", ((*S)->IsTrusted() ? "yes" : "no")));
+ pkgCache::RlsFileIterator const RlsFile = (*S)->FindInCache(*Cache, false);
+ if (RlsFile.end())
+ continue;
+#define APT_RELEASE(X,Y) if (RlsFile.Y() != NULL) AddOptions.insert(std::make_pair(X, RlsFile.Y()))
+ APT_RELEASE("CODENAME", Codename);
+ APT_RELEASE("SUITE", Archive);
+ APT_RELEASE("VERSION", Version);
+ APT_RELEASE("ORIGIN", Origin);
+ APT_RELEASE("LABEL", Label);
+#undef APT_RELEASE
+ }
+
+ for (std::vector<IndexTarget>::const_iterator T = targets.begin(); T != targets.end(); ++T)
+ {
+ std::string filename = T->Option(ReleaseInfo ? IndexTarget::EXISTING_FILENAME : IndexTarget::FILENAME);
+ if (filename.empty())
+ continue;
+
+ std::ostringstream stanza;
+ if (Filtered || Format.empty())
+ {
+ stanza << "MetaKey: " << T->MetaKey << "\n"
+ << "ShortDesc: " << T->ShortDesc << "\n"
+ << "Description: " << T->Description << "\n"
+ << "URI: " << T->URI << "\n"
+ << "Filename: " << filename << "\n"
+ << "Optional: " << (T->IsOptional ? "yes" : "no") << "\n"
+ << "KeepCompressed: " << (T->KeepCompressed ? "yes" : "no") << "\n";
+ for (std::map<std::string,std::string>::const_iterator O = AddOptions.begin(); O != AddOptions.end(); ++O)
+ stanza << format_key(O->first) << ": " << O->second << "\n";
+ for (std::map<std::string,std::string>::const_iterator O = T->Options.begin(); O != T->Options.end(); ++O)
+ {
+ if (O->first == "PDIFFS")
+ stanza << "PDiffs: " << O->second << "\n";
+ else if (O->first == "COMPRESSIONTYPES")
+ stanza << "CompressionTypes: " << O->second << "\n";
+ else if (O->first == "KEEPCOMPRESSEDAS")
+ stanza << "KeepCompressedAs: " << O->second << "\n";
+ else if (O->first == "DEFAULTENABLED")
+ stanza << "DefaultEnabled: " << O->second << "\n";
+ else
+ stanza << format_key(O->first) << ": " << O->second << "\n";
+ }
+ stanza << "\n";
+
+ if (Filtered)
+ {
+ // that is a bit crude, but good enough for now
+ bool found = true;
+ std::string haystack = std::string("\n") + stanza.str() + "\n";
+ std::transform(haystack.begin(), haystack.end(), haystack.begin(), ::tolower);
+ size_t const filesize = CmdL.FileSize() - 1;
+ for (size_t i = 0; i != filesize; ++i)
+ {
+ std::string needle = std::string("\n") + CmdL.FileList[i + 1] + "\n";
+ std::transform(needle.begin(), needle.end(), needle.begin(), ::tolower);
+ if (haystack.find(needle) != std::string::npos)
+ continue;
+ found = false;
+ break;
+ }
+ if (found == false)
+ continue;
+ }
+ }
+
+ if (Format.empty())
+ cout << stanza.str();
+ else
+ {
+ std::string out = SubstVar(Format, "$(FILENAME)", filename);
+ out = T->Format(out);
+ for (std::map<std::string,std::string>::const_iterator O = AddOptions.begin(); O != AddOptions.end(); ++O)
+ out = SubstVar(out, std::string("$(") + O->first + ")", O->second);
+ cout << out << std::endl;
+ }
+ }
+ }
+
+ return true;
+}
+ /*}}}*/
+static bool ShowHelp(CommandLine &) /*{{{*/
+{
+ if (_config->FindB("version") == true)
+ {
+ cout << _("Supported modules:") << endl;
+
+ for (unsigned I = 0; I != pkgVersioningSystem::GlobalListLen; I++)
+ {
+ pkgVersioningSystem *VS = pkgVersioningSystem::GlobalList[I];
+ if (_system != 0 && _system->VS == VS)
+ cout << '*';
+ else
+ cout << ' ';
+ cout << "Ver: " << VS->Label << endl;
+
+ /* Print out all the packaging systems that will work with
+ this VS */
+ for (unsigned J = 0; J != pkgSystem::GlobalListLen; J++)
+ {
+ pkgSystem *Sys = pkgSystem::GlobalList[J];
+ if (_system == Sys)
+ cout << '*';
+ else
+ cout << ' ';
+ if (Sys->VS->TestCompatibility(*VS) == true)
+ cout << "Pkg: " << Sys->Label << " (Priority " << Sys->Score(*_config) << ")" << endl;
+ }
+ }
+
+ for (unsigned I = 0; I != pkgSourceList::Type::GlobalListLen; I++)
+ {
+ pkgSourceList::Type *Type = pkgSourceList::Type::GlobalList[I];
+ cout << " S.L: '" << Type->Name << "' " << Type->Label << endl;
+ }
+
+ for (unsigned I = 0; I != pkgIndexFile::Type::GlobalListLen; I++)
+ {
+ pkgIndexFile::Type *Type = pkgIndexFile::Type::GlobalList[I];
+ cout << " Idx: " << Type->Label << endl;
+ }
+
+ return true;
+ }
+
+ std::cout <<
+ _("Usage: apt-get [options] command\n"
+ " apt-get [options] install|remove pkg1 [pkg2 ...]\n"
+ " apt-get [options] source pkg1 [pkg2 ...]\n"
+ "\n"
+ "apt-get is a command line interface for retrieval of packages\n"
+ "and information about them from authenticated sources and\n"
+ "for installation, upgrade and removal of packages together\n"
+ "with their dependencies.\n");
+ return true;
+}
+ /*}}}*/
+static std::vector<aptDispatchWithHelp> GetCommands() /*{{{*/
+{
+ // advanced commands are left undocumented on purpose
+ return {
+ {"update", &DoUpdate, _("Retrieve new lists of packages")},
+ {"upgrade", &DoUpgrade, _("Perform an upgrade")},
+ {"install", &DoInstall, _("Install new packages (pkg is libc6 not libc6.deb)")},
+ {"reinstall", &DoInstall, _("Reinstall packages (pkg is libc6 not libc6.deb)")},
+ {"remove", &DoInstall, _("Remove packages")},
+ {"purge", &DoInstall, _("Remove packages and config files")},
+ {"autoremove", &DoInstall, _("Remove automatically all unused packages")},
+ {"auto-remove", &DoInstall, nullptr},
+ {"autopurge",&DoInstall, nullptr},
+ {"markauto", &DoMarkAuto, nullptr},
+ {"unmarkauto", &DoMarkAuto, nullptr},
+ {"dist-upgrade", &DoDistUpgrade, _("Distribution upgrade, see apt-get(8)")},
+ {"full-upgrade", &DoDistUpgrade, nullptr},
+ {"dselect-upgrade", &DoDSelectUpgrade, _("Follow dselect selections")},
+ {"build-dep", &DoBuildDep, _("Configure build-dependencies for source packages")},
+ {"satisfy", &DoBuildDep, _("Satisfy dependency strings")},
+ {"clean", &DoClean, _("Erase downloaded archive files")},
+ {"autoclean", &DoAutoClean, _("Erase old downloaded archive files")},
+ {"auto-clean", &DoAutoClean, nullptr},
+ {"check", &DoCheck, _("Verify that there are no broken dependencies")},
+ {"source", &DoSource, _("Download source archives")},
+ {"download", &DoDownload, _("Download the binary package into the current directory")},
+ {"changelog", &DoChangelog, _("Download and display the changelog for the given package")},
+ {"indextargets", &DoIndexTargets, nullptr},
+ {"moo", &DoMoo, nullptr},
+ {nullptr, nullptr, nullptr}
+ };
+}
+ /*}}}*/
+int main(int argc,const char *argv[]) /*{{{*/
+{
+ // Parse the command line and initialize the package library
+ CommandLine CmdL;
+ auto const Cmds = ParseCommandLine(CmdL, APT_CMD::APT_GET, &_config, &_system, argc, argv, &ShowHelp, &GetCommands);
+
+ InitSignals();
+ InitOutput();
+
+ CheckIfSimulateMode(CmdL);
+
+ return DispatchCommandLine(CmdL, Cmds);
+}
+ /*}}}*/
diff --git a/cmdline/apt-helper.cc b/cmdline/apt-helper.cc
new file mode 100644
index 0000000..85795e0
--- /dev/null
+++ b/cmdline/apt-helper.cc
@@ -0,0 +1,334 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* #####################################################################
+ apt-helper - cmdline helpers
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/acquire-item.h>
+#include <apt-pkg/acquire.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/cachefilter-patterns.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/init.h>
+#include <apt-pkg/pkgsystem.h>
+#include <apt-pkg/proxy.h>
+#include <apt-pkg/strutl.h>
+
+#include <apt-pkg/srvrec.h>
+#include <apt-private/acqprogress.h>
+#include <apt-private/private-cmndline.h>
+#include <apt-private/private-download.h>
+#include <apt-private/private-main.h>
+#include <apt-private/private-output.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+static bool DoAutoDetectProxy(CommandLine &CmdL) /*{{{*/
+{
+ if (CmdL.FileSize() != 2)
+ return _error->Error(_("Need one URL as argument"));
+ URI ServerURL(CmdL.FileList[1]);
+ if (AutoDetectProxy(ServerURL) == false)
+ return false;
+ std::string SpecificProxy = _config->Find("Acquire::"+ServerURL.Access+"::Proxy::" + ServerURL.Host);
+ ioprintf(std::cout, "Using proxy '%s' for URL '%s'\n",
+ SpecificProxy.c_str(), std::string(ServerURL).c_str());
+
+ return true;
+}
+ /*}}}*/
+static bool DoDownloadFile(CommandLine &CmdL) /*{{{*/
+{
+ if (CmdL.FileSize() <= 2)
+ return _error->Error(_("Must specify at least one pair url/filename"));
+
+ aptAcquireWithTextStatus Fetcher;
+ size_t fileind = 0;
+ std::vector<std::string> targetfiles;
+ while (fileind + 2 <= CmdL.FileSize())
+ {
+ std::string download_uri = CmdL.FileList[fileind + 1];
+ std::string targetfile = CmdL.FileList[fileind + 2];
+ HashStringList hashes;
+
+ fileind += 2;
+
+ // An empty string counts as a hash for compatibility reasons
+ if (CmdL.FileSize() > fileind + 1 && *CmdL.FileList[fileind + 1] == '\0')
+ fileind++;
+
+ /* Let's start looking for hashes */
+ for (auto i = fileind + 1; CmdL.FileSize() > i; i++)
+ {
+ bool isAHash = false;
+
+ for (auto HashP = HashString::SupportedHashes(); *HashP != nullptr; HashP++)
+ {
+ if (APT::String::Startswith(CmdL.FileList[i], *HashP))
+ isAHash = true;
+ }
+
+ if (!isAHash)
+ break;
+
+ hashes.push_back(HashString(CmdL.FileList[i]));
+ fileind++;
+ }
+
+ // we use download_uri as descr and targetfile as short-descr
+ new pkgAcqFile(&Fetcher, download_uri, hashes, 0, download_uri, targetfile,
+ "dest-dir-ignored", targetfile);
+ targetfiles.push_back(targetfile);
+ }
+
+ bool Failed = false;
+ if (AcquireRun(Fetcher, 0, &Failed, NULL) == false || Failed == true)
+ return _error->Error(_("Download Failed"));
+ if (targetfiles.empty() == false)
+ for (std::vector<std::string>::const_iterator f = targetfiles.begin(); f != targetfiles.end(); ++f)
+ if (FileExists(*f) == false)
+ return _error->Error(_("Download Failed"));
+
+ return true;
+}
+ /*}}}*/
+static bool DoSrvLookup(CommandLine &CmdL) /*{{{*/
+{
+ if (CmdL.FileSize() <= 1)
+ return _error->Error("Must specify at least one SRV record");
+
+ for(size_t i = 1; CmdL.FileList[i] != NULL; ++i)
+ {
+ std::vector<SrvRec> srv_records;
+ std::string const name = CmdL.FileList[i];
+ c0out << "# Target\tPriority\tWeight\tPort # for " << name << std::endl;
+ size_t const found = name.find(":");
+ if (found != std::string::npos)
+ {
+ std::string const host = name.substr(0, found);
+ size_t const port = atoi(name.c_str() + found + 1);
+ if(GetSrvRecords(host, port, srv_records) == false)
+ _error->Error(_("GetSrvRec failed for %s"), name.c_str());
+ }
+ else if(GetSrvRecords(name, srv_records) == false)
+ _error->Error(_("GetSrvRec failed for %s"), name.c_str());
+
+ for (SrvRec const &I : srv_records)
+ ioprintf(c1out, "%s\t%d\t%d\t%d\n", I.target.c_str(), I.priority, I.weight, I.port);
+ }
+ return true;
+}
+ /*}}}*/
+static const APT::Configuration::Compressor *FindCompressor(std::vector<APT::Configuration::Compressor> const &compressors, std::string const &name) /*{{{*/
+{
+ APT::Configuration::Compressor const * compressor = NULL;
+ for (auto const & c : compressors)
+ {
+ if (compressor != NULL && c.Cost >= compressor->Cost)
+ continue;
+ if (c.Name == name || c.Extension == name || (!c.Extension.empty() && c.Extension.substr(1) == name))
+ compressor = &c;
+ }
+
+ return compressor;
+}
+ /*}}}*/
+static bool DoCatFile(CommandLine &CmdL) /*{{{*/
+{
+ FileFd fd;
+ FileFd out;
+ std::string const compressorName = _config->Find("Apt-Helper::Cat-File::Compress", "");
+
+ if (compressorName.empty() == false)
+ {
+
+ auto const compressors = APT::Configuration::getCompressors();
+ auto const compressor = FindCompressor(compressors, compressorName);
+
+ if (compressor == NULL)
+ return _error->Error("Could not find compressor: %s", compressorName.c_str());
+
+ if (out.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly, *compressor) == false)
+ return false;
+ } else
+ {
+ if (out.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly) == false)
+ return false;
+ }
+
+ if (CmdL.FileSize() <= 1)
+ {
+ if (fd.OpenDescriptor(STDIN_FILENO, FileFd::ReadOnly) == false)
+ return false;
+ if (CopyFile(fd, out) == false)
+ return false;
+ return true;
+ }
+
+ for(size_t i = 1; CmdL.FileList[i] != NULL; ++i)
+ {
+ std::string const name = CmdL.FileList[i];
+
+ if (name != "-")
+ {
+ if (fd.Open(name, FileFd::ReadOnly, FileFd::Extension) == false)
+ return false;
+ }
+ else
+ {
+ if (fd.OpenDescriptor(STDIN_FILENO, FileFd::ReadOnly) == false)
+ return false;
+ }
+
+ if (CopyFile(fd, out) == false)
+ return false;
+ }
+ return true;
+}
+ /*}}}*/
+
+static pid_t ExecuteProcess(const char *Args[]) /*{{{*/
+{
+ pid_t pid = ExecFork();
+ if (pid == 0)
+ {
+ execvp(Args[0], (char **)Args);
+ _exit(100);
+ }
+ return pid;
+}
+
+static bool ServiceIsActive(const char *service)
+{
+ const char *argv[] = {"systemctl", "is-active", "-q", service, nullptr};
+ pid_t pid = ExecuteProcess(argv);
+ return ExecWait(pid, "systemctl is-active", true);
+}
+
+static bool DoWaitOnline(CommandLine &)
+{
+ // Also add services to After= in .service
+ static const char *WaitingTasks[][6] = {
+ {"systemd-networkd.service", "/lib/systemd/systemd-networkd-wait-online", "-q", "--timeout=30", nullptr},
+ {"NetworkManager.service", "nm-online", "-q", "--timeout", "30", nullptr},
+ {"connman.service", "connmand-wait-online", "--timeout=30", nullptr},
+ };
+
+ for (const char **task : WaitingTasks)
+ {
+ if (ServiceIsActive(task[0]))
+ {
+ pid_t pid = ExecuteProcess(task + 1);
+
+ ExecWait(pid, task[1]);
+ }
+ }
+
+ return _error->PendingError() == false;
+}
+ /*}}}*/
+static bool DropPrivsAndRun(CommandLine &CmdL) /*{{{*/
+{
+ if (CmdL.FileSize() < 2)
+ return _error->Error("No command given to run without privileges");
+ if (DropPrivileges() == false)
+ return _error->Error("Dropping Privileges failed, not executing '%s'", CmdL.FileList[1]);
+
+ std::vector<char const *> Args;
+ Args.reserve(CmdL.FileSize() + 1);
+ for (auto a = CmdL.FileList + 1; *a != nullptr; ++a)
+ Args.push_back(*a);
+ Args.push_back(nullptr);
+ auto const pid = ExecuteProcess(Args.data());
+ return ExecWait(pid, CmdL.FileList[1]);
+}
+ /*}}}*/
+static bool AnalyzePattern(CommandLine &CmdL) /*{{{*/
+{
+ if (CmdL.FileSize() != 2)
+ return _error->Error("Expect one argument, a pattern");
+
+ try
+ {
+ auto top = APT::Internal::PatternTreeParser(CmdL.FileList[1]).parseTop();
+ top->render(std::cout) << "\n";
+ }
+ catch (APT::Internal::PatternTreeParser::Error &e)
+ {
+ std::stringstream ss;
+ ss << "input:" << e.location.start << "-" << e.location.end << ": error: " << e.message << "\n";
+ ss << CmdL.FileList[1] << "\n";
+ for (size_t i = 0; i < e.location.start; i++)
+ ss << " ";
+ for (size_t i = e.location.start; i < e.location.end; i++)
+ ss << "^";
+
+ ss << "\n";
+
+ _error->Error("%s", ss.str().c_str());
+ return false;
+ }
+
+ return true;
+}
+ /*}}}*/
+static bool DoQuoteString(CommandLine &CmdL) /*{{{*/
+{
+ if (CmdL.FileSize() != 3)
+ return _error->Error("Expect two arguments, a string to quote and a string of additional characters to quote");
+ std::cout << QuoteString(CmdL.FileList[1], CmdL.FileList[2]) << '\n';
+ return true;
+}
+ /*}}}*/
+static bool ShowHelp(CommandLine &) /*{{{*/
+{
+ std::cout <<
+ _("Usage: apt-helper [options] command\n"
+ " apt-helper [options] cat-file file ...\n"
+ " apt-helper [options] download-file uri target-path\n"
+ "\n"
+ "apt-helper bundles a variety of commands for shell scripts to use\n"
+ "e.g. the same proxy configuration or acquire system as APT would.\n");
+ return true;
+}
+ /*}}}*/
+static std::vector<aptDispatchWithHelp> GetCommands() /*{{{*/
+{
+ return {
+ {"download-file", &DoDownloadFile, _("download the given uri to the target-path")},
+ {"srv-lookup", &DoSrvLookup, _("lookup a SRV record (e.g. _http._tcp.ftp.debian.org)")},
+ {"cat-file", &DoCatFile, _("concatenate files, with automatic decompression")},
+ {"auto-detect-proxy", &DoAutoDetectProxy, _("detect proxy using apt.conf")},
+ {"wait-online", &DoWaitOnline, _("wait for system to be online")},
+ {"drop-privs", &DropPrivsAndRun, _("drop privileges before running given command")},
+ {"analyze-pattern", &AnalyzePattern, _("analyse a pattern")},
+ {"analyse-pattern", &AnalyzePattern, nullptr},
+ {"quote-string", &DoQuoteString, nullptr},
+ {nullptr, nullptr, nullptr}};
+}
+ /*}}}*/
+int main(int argc,const char *argv[]) /*{{{*/
+{
+ CommandLine CmdL;
+ auto const Cmds = ParseCommandLine(CmdL, APT_CMD::APT_HELPER, &_config, &_system, argc, argv, &ShowHelp, &GetCommands);
+
+ InitOutput();
+
+ return DispatchCommandLine(CmdL, Cmds);
+}
+ /*}}}*/
diff --git a/cmdline/apt-internal-planner.cc b/cmdline/apt-internal-planner.cc
new file mode 100644
index 0000000..be9de4e
--- /dev/null
+++ b/cmdline/apt-internal-planner.cc
@@ -0,0 +1,195 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* #####################################################################
+
+ cover around the internal solver to be able to run it like an external
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/cacheset.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/depcache.h>
+#include <apt-pkg/edsp.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/init.h>
+#include <apt-pkg/packagemanager.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/pkgsystem.h>
+#include <apt-pkg/prettyprinters.h>
+#include <apt-pkg/strutl.h>
+
+#include <apt-private/private-cmndline.h>
+#include <apt-private/private-main.h>
+#include <apt-private/private-output.h>
+
+#include <cstdio>
+#include <iostream>
+#include <list>
+#include <sstream>
+#include <string>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+static bool ShowHelp(CommandLine &) /*{{{*/
+{
+ std::cout <<
+ _("Usage: apt-internal-planner\n"
+ "\n"
+ "apt-internal-planner is an interface to use the current internal\n"
+ "installation planner for the APT family like an external one,\n"
+ "for debugging or the like.\n");
+ return true;
+}
+ /*}}}*/
+APT_NORETURN static void DIE(std::string const &message) { /*{{{*/
+ std::cerr << "ERROR: " << message << std::endl;
+ _error->DumpErrors(std::cerr);
+ exit(EXIT_FAILURE);
+}
+ /*}}}*/
+static std::vector<aptDispatchWithHelp> GetCommands() /*{{{*/
+{
+ return {};
+}
+ /*}}}*/
+class PMOutput: public pkgPackageManager /*{{{*/
+{
+ FileFd &output;
+ bool const Debug;
+
+protected:
+ virtual bool Install(PkgIterator Pkg,std::string) APT_OVERRIDE
+ {
+ //std::cerr << "INSTALL: " << APT::PrettyPkg(&Cache, Pkg) << std::endl;
+ return EDSP::WriteSolutionStanza(output, "Unpack", Cache[Pkg].InstVerIter(Cache));
+ }
+ virtual bool Configure(PkgIterator Pkg) APT_OVERRIDE
+ {
+ //std::cerr << "CONFIGURE: " << APT::PrettyPkg(&Cache, Pkg) << " " << std::endl;
+ return EDSP::WriteSolutionStanza(output, "Configure", Cache[Pkg].InstVerIter(Cache));
+ }
+ virtual bool Remove(PkgIterator Pkg,bool) APT_OVERRIDE
+ {
+ //std::cerr << "REMOVE: " << APT::PrettyPkg(&Cache, Pkg) << " " << std::endl;
+ return EDSP::WriteSolutionStanza(output, "Remove", Pkg.CurrentVer());
+ }
+public:
+ PMOutput(pkgDepCache *Cache, FileFd &file) : pkgPackageManager(Cache), output(file),
+ Debug(_config->FindB("Debug::EDSP::WriteSolution", false))
+ {}
+
+ bool ApplyRequest(std::list<std::pair<std::string,EIPP::PKG_ACTION>> const &actions)
+ {
+ for (auto && a: actions)
+ {
+ auto const Pkg = Cache.FindPkg(a.first);
+ if (unlikely(Pkg.end() == true))
+ continue;
+ switch (a.second)
+ {
+ case EIPP::PKG_ACTION::NOOP:
+ break;
+ case EIPP::PKG_ACTION::INSTALL:
+ case EIPP::PKG_ACTION::REINSTALL:
+ FileNames[Pkg->ID] = "EIPP";
+ break;
+ case EIPP::PKG_ACTION::REMOVE:
+ break;
+ }
+ }
+ return true;
+ }
+};
+ /*}}}*/
+int main(int argc,const char *argv[]) /*{{{*/
+{
+ // we really don't need anything
+ DropPrivileges();
+
+ CommandLine CmdL;
+ ParseCommandLine(CmdL, APT_CMD::APT_INTERNAL_PLANNER, &_config, NULL, argc, argv, &ShowHelp, &GetCommands);
+
+ // Deal with stdout not being a tty
+ if (!isatty(STDOUT_FILENO) && _config->FindI("quiet", -1) == -1)
+ _config->Set("quiet","1");
+
+ if (_config->FindI("quiet", 0) < 1)
+ _config->Set("Debug::EIPP::WriteSolution", true);
+
+ _config->Set("APT::System", "Debian APT planner interface");
+ _config->Set("APT::Planner", "internal");
+ _config->Set("eipp::scenario", "/nonexistent/stdin");
+ FileFd output;
+ if (output.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false)
+ DIE("stdout couldn't be opened");
+ int const input = STDIN_FILENO;
+ SetNonBlock(input, false);
+
+ EDSP::WriteProgress(0, "Start up planner…", output);
+
+ if (pkgInitSystem(*_config,_system) == false)
+ DIE("System could not be initialized!");
+
+ EDSP::WriteProgress(1, "Read request…", output);
+
+ if (WaitFd(input, false, 5) == false)
+ DIE("WAIT timed out in the planner");
+
+ std::list<std::pair<std::string,EIPP::PKG_ACTION>> actions;
+ unsigned int flags;
+ if (EIPP::ReadRequest(input, actions, flags) == false)
+ DIE("Parsing the request failed!");
+ _config->Set("APT::Immediate-Configure", (flags & EIPP::Request::NO_IMMEDIATE_CONFIGURATION) == 0);
+ _config->Set("APT::Immediate-Configure-All", (flags & EIPP::Request::IMMEDIATE_CONFIGURATION_ALL) != 0);
+ _config->Set("APT::Force-LoopBreak", (flags & EIPP::Request::ALLOW_TEMPORARY_REMOVE_OF_ESSENTIALS) != 0);
+
+ EDSP::WriteProgress(5, "Read scenario…", output);
+
+ pkgCacheFile CacheFile;
+ if (CacheFile.Open(NULL, false) == false)
+ DIE("Failed to open CacheFile!");
+
+ EDSP::WriteProgress(50, "Apply request on scenario…", output);
+
+ if (EIPP::ApplyRequest(actions, CacheFile) == false)
+ DIE("Failed to apply request to depcache!");
+
+ EDSP::WriteProgress(60, "Call orderinstall on current scenario…", output);
+
+ //_config->Set("Debug::pkgOrderList", true);
+ //_config->Set("Debug::pkgPackageManager", true);
+ PMOutput PM(CacheFile, output);
+ if (PM.ApplyRequest(actions) == false)
+ DIE("Failed to apply request to packagemanager!");
+ pkgPackageManager::OrderResult const Res = PM.DoInstallPreFork();
+ std::ostringstream broken;
+ switch (Res)
+ {
+ case pkgPackageManager::Completed:
+ EDSP::WriteProgress(100, "Done", output);
+ break;
+ case pkgPackageManager::Incomplete:
+ broken << "Planner could only incompletely plan an installation order!" << std::endl;
+ _error->DumpErrors(broken, GlobalError::DEBUG);
+ EDSP::WriteError("pm-incomplete", broken.str(), output);
+ break;
+ case pkgPackageManager::Failed:
+ broken << "Planner failed to find an installation order!" << std::endl;
+ _error->DumpErrors(broken, GlobalError::DEBUG);
+ EDSP::WriteError("pm-failed", broken.str(), output);
+ break;
+ }
+
+ return DispatchCommandLine(CmdL, {});
+}
+ /*}}}*/
diff --git a/cmdline/apt-internal-solver.cc b/cmdline/apt-internal-solver.cc
new file mode 100644
index 0000000..23af6da
--- /dev/null
+++ b/cmdline/apt-internal-solver.cc
@@ -0,0 +1,214 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* #####################################################################
+
+ cover around the internal solver to be able to run it like an external
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/algorithms.h>
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/cacheset.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/depcache.h>
+#include <apt-pkg/edsp.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/init.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/pkgsystem.h>
+#include <apt-pkg/strutl.h>
+#include <apt-pkg/upgrade.h>
+
+#include <apt-private/private-cmndline.h>
+#include <apt-private/private-main.h>
+#include <apt-private/private-output.h>
+
+#include <cstdio>
+#include <iostream>
+#include <list>
+#include <sstream>
+#include <string>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+static bool ShowHelp(CommandLine &) /*{{{*/
+{
+ std::cout <<
+ _("Usage: apt-internal-solver\n"
+ "\n"
+ "apt-internal-solver is an interface to use the current internal\n"
+ "resolver for the APT family like an external one, for debugging or\n"
+ "the like.\n");
+ return true;
+}
+ /*}}}*/
+APT_NORETURN static void DIE(std::string const &message) { /*{{{*/
+ std::cerr << "ERROR: " << message << std::endl;
+ _error->DumpErrors(std::cerr);
+ exit(EXIT_FAILURE);
+}
+ /*}}}*/
+static std::vector<aptDispatchWithHelp> GetCommands() /*{{{*/
+{
+ return {};
+}
+ /*}}}*/
+static bool WriteSolution(pkgDepCache &Cache, FileFd &output) /*{{{*/
+{
+ bool Okay = output.Failed() == false;
+ for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false && likely(Okay); ++Pkg)
+ {
+ if (Cache[Pkg].Delete() == true)
+ Okay &= EDSP::WriteSolutionStanza(output, "Remove", Pkg.CurrentVer());
+ else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
+ Okay &= EDSP::WriteSolutionStanza(output, "Install", Cache.GetCandidateVersion(Pkg));
+ else if (Cache[Pkg].Garbage == true)
+ Okay &= EDSP::WriteSolutionStanza(output, "Autoremove", Pkg.CurrentVer());
+ }
+ return Okay;
+}
+ /*}}}*/
+int main(int argc,const char *argv[]) /*{{{*/
+{
+ // we really don't need anything
+ DropPrivileges();
+
+ CommandLine CmdL;
+ ParseCommandLine(CmdL, APT_CMD::APT_INTERNAL_SOLVER, &_config, NULL, argc, argv, &ShowHelp, &GetCommands);
+
+ if (CmdL.FileList[0] != 0 && strcmp(CmdL.FileList[0], "scenario") == 0)
+ {
+ if (pkgInitSystem(*_config,_system) == false) {
+ std::cerr << "System could not be initialized!" << std::endl;
+ return 1;
+ }
+ pkgCacheFile CacheFile;
+ CacheFile.Open(NULL, false);
+ APT::PackageSet pkgset = APT::PackageSet::FromCommandLine(CacheFile, CmdL.FileList + 1);
+ FileFd output;
+ if (output.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false)
+ return 2;
+ if (pkgset.empty() == true)
+ EDSP::WriteScenario(CacheFile, output);
+ else
+ {
+ std::vector<bool> pkgvec(CacheFile->Head().PackageCount, false);
+ for (auto const &p: pkgset)
+ pkgvec[p->ID] = true;
+ EDSP::WriteLimitedScenario(CacheFile, output, pkgvec);
+ }
+ output.Close();
+ _error->DumpErrors(std::cerr);
+ return 0;
+ }
+
+ // Deal with stdout not being a tty
+ if (!isatty(STDOUT_FILENO) && _config->FindI("quiet", -1) == -1)
+ _config->Set("quiet","1");
+
+ if (_config->FindI("quiet", 0) < 1)
+ _config->Set("Debug::EDSP::WriteSolution", true);
+
+ _config->Set("APT::System", "Debian APT solver interface");
+ _config->Set("APT::Solver", "internal");
+ _config->Set("edsp::scenario", "/nonexistent/stdin");
+ _config->Clear("Dir::Log");
+ FileFd output;
+ if (output.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false)
+ DIE("stdout couldn't be opened");
+ int const input = STDIN_FILENO;
+ SetNonBlock(input, false);
+
+ EDSP::WriteProgress(0, "Start up solver…", output);
+
+ if (pkgInitSystem(*_config,_system) == false)
+ DIE("System could not be initialized!");
+
+ EDSP::WriteProgress(1, "Read request…", output);
+
+ if (WaitFd(input, false, 5) == false)
+ DIE("WAIT timed out in the resolver");
+
+ std::list<std::string> install, remove;
+ unsigned int flags;
+ if (EDSP::ReadRequest(input, install, remove, flags) == false)
+ DIE("Parsing the request failed!");
+
+ EDSP::WriteProgress(5, "Read scenario…", output);
+
+ pkgCacheFile CacheFile;
+ CacheFile.InhibitActionGroups(true);
+ if (CacheFile.Open(NULL, false) == false)
+ DIE("Failed to open CacheFile!");
+
+ EDSP::WriteProgress(50, "Apply request on scenario…", output);
+
+ if (EDSP::ApplyRequest(install, remove, CacheFile) == false)
+ DIE("Failed to apply request to depcache!");
+
+ pkgProblemResolver Fix(CacheFile);
+ for (std::list<std::string>::const_iterator i = remove.begin();
+ i != remove.end(); ++i) {
+ pkgCache::PkgIterator P = CacheFile->FindPkg(*i);
+ Fix.Clear(P);
+ Fix.Protect(P);
+ Fix.Remove(P);
+ }
+
+ for (std::list<std::string>::const_iterator i = install.begin();
+ i != install.end(); ++i) {
+ pkgCache::PkgIterator P = CacheFile->FindPkg(*i);
+ Fix.Clear(P);
+ Fix.Protect(P);
+ }
+
+ for (std::list<std::string>::const_iterator i = install.begin();
+ i != install.end(); ++i)
+ CacheFile->MarkInstall(CacheFile->FindPkg(*i), true);
+
+ EDSP::WriteProgress(60, "Call problemresolver on current scenario…", output);
+
+ std::string failure;
+ if (flags & EDSP::Request::UPGRADE_ALL) {
+ int upgrade_flags = APT::Upgrade::ALLOW_EVERYTHING;
+ if (flags & EDSP::Request::FORBID_NEW_INSTALL)
+ upgrade_flags |= APT::Upgrade::FORBID_INSTALL_NEW_PACKAGES;
+ if (flags & EDSP::Request::FORBID_REMOVE)
+ upgrade_flags |= APT::Upgrade::FORBID_REMOVE_PACKAGES;
+
+ if (APT::Upgrade::Upgrade(CacheFile, upgrade_flags))
+ ;
+ else if (upgrade_flags == APT::Upgrade::ALLOW_EVERYTHING)
+ failure = "ERR_UNSOLVABLE_FULL_UPGRADE";
+ else
+ failure = "ERR_UNSOLVABLE_UPGRADE";
+ } else if (Fix.Resolve() == false)
+ failure = "ERR_UNSOLVABLE";
+
+ if (failure.empty() == false) {
+ std::ostringstream broken;
+ ShowBroken(broken, CacheFile, false);
+ EDSP::WriteError(failure.c_str(), broken.str(), output);
+ return 0;
+ }
+
+ EDSP::WriteProgress(95, "Write solution…", output);
+
+ CacheFile->MarkAndSweep();
+ if (WriteSolution(CacheFile, output) == false)
+ DIE("Failed to output the solution!");
+
+ EDSP::WriteProgress(100, "Done", output);
+
+ return DispatchCommandLine(CmdL, {});
+}
+ /*}}}*/
diff --git a/cmdline/apt-key.in b/cmdline/apt-key.in
new file mode 100644
index 0000000..80b0c2a
--- /dev/null
+++ b/cmdline/apt-key.in
@@ -0,0 +1,831 @@
+#!/bin/sh
+
+set -e
+unset GREP_OPTIONS GPGHOMEDIR CURRENTTRAP
+export IFS="$(printf "\n\b")"
+
+MASTER_KEYRING='&keyring-master-filename;'
+eval "$(apt-config shell MASTER_KEYRING APT::Key::MasterKeyring)"
+ARCHIVE_KEYRING='&keyring-filename;'
+eval "$(apt-config shell ARCHIVE_KEYRING APT::Key::ArchiveKeyring)"
+REMOVED_KEYS='&keyring-removed-filename;'
+eval "$(apt-config shell REMOVED_KEYS APT::Key::RemovedKeys)"
+ARCHIVE_KEYRING_URI='&keyring-uri;'
+eval "$(apt-config shell ARCHIVE_KEYRING_URI APT::Key::ArchiveKeyringURI)"
+
+aptkey_echo() { echo "$@"; }
+
+find_gpgv_status_fd() {
+ while [ -n "$1" ]; do
+ if [ "$1" = '--status-fd' ]; then
+ shift
+ echo "$1"
+ break
+ fi
+ shift
+ done
+}
+GPGSTATUSFD="$(find_gpgv_status_fd "$@")"
+
+apt_warn() {
+ if [ -z "$GPGHOMEDIR" ]; then
+ echo >&2 'W:' "$@"
+ else
+ echo 'W:' "$@" > "${GPGHOMEDIR}/aptwarnings.log"
+ fi
+ if [ -n "$GPGSTATUSFD" ]; then
+ echo >&${GPGSTATUSFD} '[APTKEY:] WARNING' "$@"
+ fi
+}
+apt_error() {
+ if [ -z "$GPGHOMEDIR" ]; then
+ echo >&2 'E:' "$@"
+ else
+ echo 'E:' "$@" > "${GPGHOMEDIR}/aptwarnings.log"
+ fi
+ if [ -n "$GPGSTATUSFD" ]; then
+ echo >&${GPGSTATUSFD} '[APTKEY:] ERROR' "$@"
+ fi
+}
+
+cleanup_gpg_home() {
+ if [ -z "$GPGHOMEDIR" ]; then return; fi
+ if [ -s "$GPGHOMEDIR/aptwarnings.log" ]; then
+ cat >&2 "$GPGHOMEDIR/aptwarnings.log"
+ fi
+ if command_available 'gpgconf'; then
+ GNUPGHOME="${GPGHOMEDIR}" gpgconf --kill all >/dev/null 2>&1 || true
+ fi
+ rm -rf "$GPGHOMEDIR"
+}
+
+# gpg needs (in different versions more or less) files to function correctly,
+# so we give it its own homedir and generate some valid content for it later on
+create_gpg_home() {
+ # for cases in which we want to cache a homedir due to expensive setup
+ if [ -n "$GPGHOMEDIR" ]; then
+ return
+ fi
+ if [ -n "$TMPDIR" ]; then
+ # tmpdir is a directory and current user has rwx access to it
+ # same tests as in apt-pkg/contrib/fileutl.cc GetTempDir()
+ if [ ! -d "$TMPDIR" ] || [ ! -r "$TMPDIR" ] || [ ! -w "$TMPDIR" ] || [ ! -x "$TMPDIR" ]; then
+ unset TMPDIR
+ fi
+ fi
+ GPGHOMEDIR="$(mktemp --directory --tmpdir 'apt-key-gpghome.XXXXXXXXXX')"
+ CURRENTTRAP="${CURRENTTRAP} cleanup_gpg_home;"
+ trap "${CURRENTTRAP}" 0 HUP INT QUIT ILL ABRT FPE SEGV PIPE TERM
+ if [ -z "$GPGHOMEDIR" ]; then
+ apt_error "Could not create temporary gpg home directory in $TMPDIR (wrong permissions?)"
+ exit 28
+ fi
+ chmod 700 "$GPGHOMEDIR"
+}
+
+requires_root() {
+ if [ "$(id -u)" -ne 0 ]; then
+ apt_error "This command can only be used by root."
+ exit 1
+ fi
+}
+
+command_available() {
+ if [ -x "$1" ]; then return 0; fi
+ command -v "$1" >/dev/null # required by policy, see #747320
+}
+
+escape_shell() {
+ echo "$@" | sed -e "s#'#'\"'\"'#g"
+}
+
+get_fingerprints_of_keyring() {
+ aptkey_execute "$GPG_SH" --keyring "$1" --with-colons --fingerprint | while read publine; do
+ # search for a public key
+ if [ "${publine%%:*}" != 'pub' ]; then continue; fi
+ # search for the associated fingerprint (should be the very next line)
+ while read fprline; do
+ if [ "${fprline%%:*}" = 'sub' ]; then break; # should never happen
+ elif [ "${fprline%%:*}" != 'fpr' ]; then continue; fi
+ echo "$fprline" | cut -d':' -f 10
+ done
+ # order in the keyring shouldn't be important
+ done | sort
+}
+
+add_keys_with_verify_against_master_keyring() {
+ ADD_KEYRING="$1"
+ MASTER="$2"
+
+ if [ ! -f "$ADD_KEYRING" ]; then
+ apt_error "Keyring '$ADD_KEYRING' to be added not found"
+ return
+ fi
+ if [ ! -f "$MASTER" ]; then
+ apt_error "Master-Keyring '$MASTER' not found"
+ return
+ fi
+
+ # when adding new keys, make sure that the archive-master-keyring
+ # is honored. so:
+ # all keys that are exported must have a valid signature
+ # from a key in the $distro-master-keyring
+ add_keys="$(get_fingerprints_of_keyring "$ADD_KEYRING")"
+ all_add_keys="$(aptkey_execute "$GPG_SH" --keyring "$ADD_KEYRING" --with-colons --list-keys | grep ^[ps]ub | cut -d: -f5)"
+ master_keys="$(aptkey_execute "$GPG_SH" --keyring "$MASTER" --with-colons --list-keys | grep ^pub | cut -d: -f5)"
+
+ # ensure there are no colisions LP: #857472
+ for all_add_key in $all_add_keys; do
+ for master_key in $master_keys; do
+ if [ "$all_add_key" = "$master_key" ]; then
+ echo >&2 "Keyid collision for '$all_add_key' detected, operation aborted"
+ return 1
+ fi
+ done
+ done
+
+ for add_key in $add_keys; do
+ # export the add keyring one-by-one
+ local TMP_KEYRING="${GPGHOMEDIR}/tmp-keyring.gpg"
+ aptkey_execute "$GPG_SH" --batch --yes --keyring "$ADD_KEYRING" --output "$TMP_KEYRING" --export "$add_key"
+ if ! aptkey_execute "$GPG_SH" --batch --yes --keyring "$TMP_KEYRING" --import "$MASTER" > "${GPGHOMEDIR}/gpgoutput.log" 2>&1; then
+ cat >&2 "${GPGHOMEDIR}/gpgoutput.log"
+ false
+ fi
+ # check if signed with the master key and only add in this case
+ ADDED=0
+ for master_key in $master_keys; do
+ if aptkey_execute "$GPG_SH" --keyring "$TMP_KEYRING" --check-sigs --with-colons "$add_key" \
+ | grep '^sig:!:' | cut -d: -f5 | grep -q "$master_key"; then
+ aptkey_execute "$GPG_SH" --batch --yes --keyring "$ADD_KEYRING" --export "$add_key" \
+ | aptkey_execute "$GPG" --batch --yes --import
+ ADDED=1
+ fi
+ done
+ if [ $ADDED = 0 ]; then
+ echo >&2 "Key '$add_key' not added. It is not signed with a master key"
+ fi
+ rm -f "${TMP_KEYRING}"
+ done
+}
+
+# update the current archive signing keyring from a network URI
+# the archive-keyring keys needs to be signed with the master key
+# (otherwise it does not make sense from a security POV)
+net_update() {
+ local APT_DIR='/'
+ eval $(apt-config shell APT_DIR Dir)
+
+ # Disabled for now as code is insecure (LP: #1013639 (and 857472, 1013128))
+ APT_KEY_NET_UPDATE_ENABLED=""
+ eval $(apt-config shell APT_KEY_NET_UPDATE_ENABLED APT::Key::Net-Update-Enabled)
+ if [ -z "$APT_KEY_NET_UPDATE_ENABLED" ]; then
+ exit 1
+ fi
+
+ if [ -z "$ARCHIVE_KEYRING_URI" ]; then
+ apt_error 'Your distribution is not supported in net-update as no uri for the archive-keyring is set'
+ exit 1
+ fi
+ # in theory we would need to depend on wget for this, but this feature
+ # isn't usable in debian anyway as we have no keyring uri nor a master key
+ if ! command_available 'wget'; then
+ apt_error 'wget is required for a network-based update, but it is not installed'
+ exit 1
+ fi
+ if [ ! -d "${APT_DIR}/var/lib/apt/keyrings" ]; then
+ mkdir -p "${APT_DIR}/var/lib/apt/keyrings"
+ fi
+ keyring="${APT_DIR}/var/lib/apt/keyrings/$(basename "$ARCHIVE_KEYRING_URI")"
+ old_mtime=0
+ if [ -e $keyring ]; then
+ old_mtime=$(stat -c %Y "$keyring")
+ fi
+ (cd "${APT_DIR}/var/lib/apt/keyrings"; wget --timeout=90 -q -N "$ARCHIVE_KEYRING_URI")
+ if [ ! -e "$keyring" ]; then
+ return
+ fi
+ new_mtime=$(stat -c %Y "$keyring")
+ if [ $new_mtime -ne $old_mtime ]; then
+ aptkey_echo "Checking for new archive signing keys now"
+ add_keys_with_verify_against_master_keyring "$keyring" "$MASTER_KEYRING"
+ fi
+}
+
+update() {
+ if [ -z "$APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE" ]; then
+ echo >&2 "Warning: 'apt-key update' is deprecated and should not be used anymore!"
+ if [ -z "$ARCHIVE_KEYRING" ]; then
+ echo >&2 "Note: In your distribution this command is a no-op and can therefore be removed safely."
+ exit 0
+ fi
+ fi
+ if [ ! -f "$ARCHIVE_KEYRING" ]; then
+ apt_error "Can't find the archive-keyring (Is the &keyring-package; package installed?)"
+ exit 1
+ fi
+
+ # add new keys from the package;
+
+ # we do not use add_keys_with_verify_against_master_keyring here,
+ # because "update" is run on regular package updates. A
+ # attacker might as well replace the master-archive-keyring file
+ # in the package and add his own keys. so this check wouldn't
+ # add any security. we *need* this check on net-update though
+ import_keyring_into_keyring "$ARCHIVE_KEYRING" '' && cat "${GPGHOMEDIR}/gpgoutput.log"
+
+ if [ -r "$REMOVED_KEYS" ]; then
+ # remove no-longer supported/used keys
+ get_fingerprints_of_keyring "$(dearmor_filename "$REMOVED_KEYS")" | while read key; do
+ foreach_keyring_do 'remove_key_from_keyring' "$key"
+ done
+ else
+ apt_warn "Removed keys keyring '$REMOVED_KEYS' missing or not readable"
+ fi
+}
+
+remove_key_from_keyring() {
+ local KEYRINGFILE="$1"
+ shift
+ # non-existent keyrings have by definition no keys
+ if [ ! -e "$KEYRINGFILE" ]; then
+ return
+ fi
+
+ local FINGERPRINTS="${GPGHOMEDIR}/keyringfile.keylst"
+ local DEARMOR="$(dearmor_filename "$KEYRINGFILE")"
+ get_fingerprints_of_keyring "$DEARMOR" > "$FINGERPRINTS"
+
+ for KEY in "$@"; do
+ # strip leading 0x, if present:
+ KEY="$(echo "${KEY#0x}" | tr -d ' ')"
+
+ # check if the key is in this keyring
+ if ! grep -iq "^[0-9A-F]*${KEY}$" "$FINGERPRINTS"; then
+ continue
+ fi
+ if [ ! -w "$KEYRINGFILE" ]; then
+ apt_warn "Key ${KEY} is in keyring ${KEYRINGFILE}, but can't be removed as it is read only."
+ continue
+ fi
+ # check if it is the only key in the keyring and if so remove the keyring altogether
+ if [ '1' = "$(uniq "$FINGERPRINTS" | wc -l)" ]; then
+ mv -f "$KEYRINGFILE" "${KEYRINGFILE}~" # behave like gpg
+ return
+ fi
+ # we can't just modify pointed to files as these might be in /usr or something
+ local REALTARGET
+ if [ -L "$DEARMOR" ]; then
+ REALTARGET="$(readlink -f "$DEARMOR")"
+ mv -f "$DEARMOR" "${DEARMOR}.dpkg-tmp"
+ cp -a "$REALTARGET" "$DEARMOR"
+ fi
+ # delete the key from the keyring
+ aptkey_execute "$GPG_SH" --keyring "$DEARMOR" --batch --delete-keys --yes "$KEY"
+ if [ -n "$REALTARGET" ]; then
+ # the real backup is the old link, not the copy we made
+ mv -f "${DEARMOR}.dpkg-tmp" "${DEARMOR}~"
+ fi
+ if [ "$DEARMOR" != "$KEYRINGFILE" ]; then
+ mv -f "$KEYRINGFILE" "${KEYRINGFILE}~"
+ create_new_keyring "$KEYRINGFILE"
+ aptkey_execute "$GPG_SH" --keyring "$DEARMOR" --armor --export > "$KEYRINGFILE"
+ fi
+ get_fingerprints_of_keyring "$DEARMOR" > "$FINGERPRINTS"
+ done
+}
+
+accessible_file_exists() {
+ if ! test -s "$1"; then
+ return 1
+ fi
+ if test -r "$1"; then
+ return 0
+ fi
+ apt_warn "The key(s) in the keyring $1 are ignored as the file is not readable by user '$USER' executing apt-key."
+ return 1
+}
+
+is_supported_keyring() {
+ # empty files are always supported
+ if ! test -s "$1"; then
+ return 0
+ fi
+ local FILEEXT="${1##*.}"
+ if [ "$FILEEXT" = 'gpg' ]; then
+ # 0x98, 0x99 and 0xC6 via octal as hex isn't supported by dashs printf
+ if printf '\231' | cmp -s -n 1 - "$1"; then
+ true
+ elif printf '\230' | cmp -s -n 1 - "$1"; then
+ true
+ elif printf '\306' | cmp -s -n 1 - "$1"; then
+ true
+ else
+ apt_warn "The key(s) in the keyring $1 are ignored as the file has an unsupported filetype."
+ return 1
+ fi
+ elif [ "$FILEEXT" = 'asc' ]; then
+ true #dearmor_filename will deal with them
+ else
+ # most callers ignore unsupported extensions silently
+ apt_warn "The key(s) in the keyring $1 are ignored as the file has an unsupported filename extension."
+ return 1
+ fi
+ return 0
+}
+
+foreach_keyring_do() {
+ local ACTION="$1"
+ shift
+ # if a --keyring was given, just work on this one
+ if [ -n "$FORCED_KEYRING" ]; then
+ $ACTION "$FORCED_KEYRING" "$@"
+ else
+ # otherwise all known keyrings are up for inspection
+ if accessible_file_exists "$TRUSTEDFILE" && is_supported_keyring "$TRUSTEDFILE"; then
+ $ACTION "$TRUSTEDFILE" "$@"
+ fi
+ local TRUSTEDPARTS="/etc/apt/trusted.gpg.d"
+ eval "$(apt-config shell TRUSTEDPARTS Dir::Etc::TrustedParts/d)"
+ if [ -d "$TRUSTEDPARTS" ]; then
+ TRUSTEDPARTS="$(readlink -f "$TRUSTEDPARTS")"
+ local TRUSTEDPARTSLIST="$(cd /; find "$TRUSTEDPARTS" -mindepth 1 -maxdepth 1 \( -name '*.gpg' -o -name '*.asc' \))"
+ for trusted in $(echo "$TRUSTEDPARTSLIST" | sort); do
+ if accessible_file_exists "$trusted" && is_supported_keyring "$trusted"; then
+ $ACTION "$trusted" "$@"
+ fi
+ done
+ fi
+ fi
+}
+
+list_keys_in_keyring() {
+ local KEYRINGFILE="$1"
+ shift
+ # fingerprint and co will fail if key isn't in this keyring
+ aptkey_execute "$GPG_SH" --keyring "$(dearmor_filename "$KEYRINGFILE")" "$@" > "${GPGHOMEDIR}/gpgoutput.log" 2> "${GPGHOMEDIR}/gpgoutput.err" || true
+ if [ ! -s "${GPGHOMEDIR}/gpgoutput.log" ]; then
+ return
+ fi
+ # we fake gpg header here to refer to the real asc file rather than a temp file
+ if [ "${KEYRINGFILE##*.}" = 'asc' ]; then
+ if expr match "$(sed -n '2p' "${GPGHOMEDIR}/gpgoutput.log")" '^-\+$' >/dev/null 2>&1; then
+ echo "$KEYRINGFILE"
+ echo "$KEYRINGFILE" | sed 's#[^-]#-#g'
+ sed '1,2d' "${GPGHOMEDIR}/gpgoutput.log" || true
+ else
+ cat "${GPGHOMEDIR}/gpgoutput.log"
+ fi
+ else
+ cat "${GPGHOMEDIR}/gpgoutput.log"
+ fi
+ if [ -s "${GPGHOMEDIR}/gpgoutput.err" ]; then
+ cat >&2 "${GPGHOMEDIR}/gpgoutput.err"
+ fi
+}
+
+export_key_from_to() {
+ local FROM="$1"
+ local TO="$2"
+ shift 2
+ if ! aptkey_execute "$GPG_SH" --keyring "$(dearmor_filename "$FROM")" --export "$@" > "$TO" 2> "${GPGHOMEDIR}/gpgoutput.log"; then
+ cat >&2 "${GPGHOMEDIR}/gpgoutput.log"
+ false
+ else
+ chmod 0644 -- "$TO"
+ fi
+}
+
+import_keyring_into_keyring() {
+ local FROM="${1:-${GPGHOMEDIR}/pubring.gpg}"
+ local TO="${2:-${GPGHOMEDIR}/pubring.gpg}"
+ shift 2
+ rm -f "${GPGHOMEDIR}/gpgoutput.log"
+ # the idea is simple: We take keys from one keyring and copy it to another
+ # we do this with so many checks in between to ensure that WE control the
+ # creation, so we know that the (potentially) created $TO keyring is a
+ # simple keyring rather than a keybox as gpg2 would create it which in turn
+ # can't be read by gpgv.
+ # BEWARE: This is designed more in the way to work with the current
+ # callers, than to have a well defined it would be easy to add new callers to.
+ if [ ! -s "$TO" ]; then
+ if [ -s "$FROM" ]; then
+ if [ -z "$2" ]; then
+ local OPTS
+ if [ "${TO##*.}" = 'asc' ]; then
+ OPTS='--armor'
+ fi
+ export_key_from_to "$(dearmor_filename "$FROM")" "$TO" $OPTS ${1:+"$1"}
+ else
+ create_new_keyring "$TO"
+ fi
+ else
+ create_new_keyring "$TO"
+ fi
+ elif [ -s "$FROM" ]; then
+ local EXPORTLIMIT="$1"
+ if [ -n "$1$2" ]; then shift; fi
+ local DEARMORTO="$(dearmor_filename "$TO")"
+ if ! aptkey_execute "$GPG_SH" --keyring "$(dearmor_filename "$FROM")" --export ${EXPORTLIMIT:+"$EXPORTLIMIT"} \
+ | aptkey_execute "$GPG_SH" --keyring "$DEARMORTO" --batch --import "$@" > "${GPGHOMEDIR}/gpgoutput.log" 2>&1; then
+ cat >&2 "${GPGHOMEDIR}/gpgoutput.log"
+ false
+ fi
+ if [ "$DEARMORTO" != "$TO" ]; then
+ export_key_from_to "$DEARMORTO" "${DEARMORTO}.asc" --armor
+ if ! cmp -s "$TO" "${DEARMORTO}.asc" 2>/dev/null; then
+ cp -a "$TO" "${TO}~"
+ mv -f "${DEARMORTO}.asc" "$TO"
+ fi
+ fi
+ fi
+}
+
+dearmor_keyring() {
+ # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=831409#67
+ # The awk script is more complex through to skip surrounding garbage and
+ # to support multiple keys in one file (old gpgs generate version headers
+ # which get printed with the original and hence result in garbage input for base64
+ awk '/^-----BEGIN/{ x = 1; }
+/^$/{ if (x == 1) { x = 2; }; }
+/^[^=-]/{ if (x == 2) { print $0; }; }
+/^-----END/{ x = 0; }' | base64 -d
+}
+dearmor_filename() {
+ if [ "${1##*.}" = 'asc' ]; then
+ local trusted="${GPGHOMEDIR}/${1##*/}.gpg"
+ if [ -s "$1" ]; then
+ dearmor_keyring < "$1" > "$trusted"
+ fi
+ echo "$trusted"
+ elif [ "${1##*.}" = 'gpg' ]; then
+ echo "$1"
+ elif [ "$(head -n 1 "$1" 2>/dev/null)" = '-----BEGIN PGP PUBLIC KEY BLOCK-----' ]; then
+ local trusted="${GPGHOMEDIR}/${1##*/}.gpg"
+ dearmor_keyring < "$1" > "$trusted"
+ echo "$trusted"
+ else
+ echo "$1"
+ fi
+}
+catfile() {
+ cat "$(dearmor_filename "$1")" >> "$2"
+}
+
+merge_all_trusted_keyrings_into_pubring() {
+ # does the same as:
+ # foreach_keyring_do 'import_keys_from_keyring' "${GPGHOMEDIR}/pubring.gpg"
+ # but without using gpg, just cat and find
+ local PUBRING="$(readlink -f "${GPGHOMEDIR}")/pubring.gpg"
+ rm -f "$PUBRING"
+ touch "$PUBRING"
+ foreach_keyring_do 'catfile' "$PUBRING"
+}
+
+import_keys_from_keyring() {
+ import_keyring_into_keyring "$1" "$2"
+}
+
+merge_keys_into_keyrings() {
+ import_keyring_into_keyring "$2" "$1" '' --import-options 'merge-only'
+}
+
+merge_back_changes() {
+ if [ -n "$FORCED_KEYRING" ]; then
+ # if the keyring was forced merge is already done
+ if [ "$FORCED_KEYRING" != "$TRUSTEDFILE" ]; then
+ mv -f "$FORCED_KEYRING" "${FORCED_KEYRING}~"
+ export_key_from_to "$TRUSTEDFILE" "$FORCED_KEYRING" --armor
+ fi
+ return
+ fi
+ if [ -s "${GPGHOMEDIR}/pubring.gpg" ]; then
+ # merge all updated keys
+ foreach_keyring_do 'merge_keys_into_keyrings' "${GPGHOMEDIR}/pubring.gpg"
+ fi
+ # look for keys which were added or removed
+ get_fingerprints_of_keyring "${GPGHOMEDIR}/pubring.orig.gpg" > "${GPGHOMEDIR}/pubring.orig.keylst"
+ get_fingerprints_of_keyring "${GPGHOMEDIR}/pubring.gpg" > "${GPGHOMEDIR}/pubring.keylst"
+ comm -3 "${GPGHOMEDIR}/pubring.keylst" "${GPGHOMEDIR}/pubring.orig.keylst" > "${GPGHOMEDIR}/pubring.diff"
+ # key isn't part of new keyring, so remove
+ cut -f 2 "${GPGHOMEDIR}/pubring.diff" | while read key; do
+ if [ -z "$key" ]; then continue; fi
+ foreach_keyring_do 'remove_key_from_keyring' "$key"
+ done
+ # key is only part of new keyring, so we need to import it
+ cut -f 1 "${GPGHOMEDIR}/pubring.diff" | while read key; do
+ if [ -z "$key" ]; then continue; fi
+ import_keyring_into_keyring '' "$TRUSTEDFILE" "$key"
+ done
+}
+
+setup_merged_keyring() {
+ if [ -n "$FORCED_KEYID" ]; then
+ merge_all_trusted_keyrings_into_pubring
+ FORCED_KEYRING="${GPGHOMEDIR}/forcedkeyid.gpg"
+ TRUSTEDFILE="${FORCED_KEYRING}"
+ echo "#!/bin/sh
+exec sh '($(escape_shell "${GPG}")' --keyring '$(escape_shell "${TRUSTEDFILE}")' \"\$@\"" > "${GPGHOMEDIR}/gpg.1.sh"
+ GPG="${GPGHOMEDIR}/gpg.1.sh"
+ # ignore error as this "just" means we haven't found the forced keyid and the keyring will be empty
+ import_keyring_into_keyring '' "$TRUSTEDFILE" "$FORCED_KEYID" || true
+ elif [ -z "$FORCED_KEYRING" ]; then
+ merge_all_trusted_keyrings_into_pubring
+ if [ -r "${GPGHOMEDIR}/pubring.gpg" ]; then
+ cp -a "${GPGHOMEDIR}/pubring.gpg" "${GPGHOMEDIR}/pubring.orig.gpg"
+ else
+ touch "${GPGHOMEDIR}/pubring.gpg" "${GPGHOMEDIR}/pubring.orig.gpg"
+ fi
+ echo "#!/bin/sh
+exec sh '$(escape_shell "${GPG}")' --keyring '$(escape_shell "${GPGHOMEDIR}/pubring.gpg")' \"\$@\"" > "${GPGHOMEDIR}/gpg.1.sh"
+ GPG="${GPGHOMEDIR}/gpg.1.sh"
+ else
+ TRUSTEDFILE="$(dearmor_filename "$FORCED_KEYRING")"
+ create_new_keyring "$TRUSTEDFILE"
+ echo "#!/bin/sh
+exec sh '$(escape_shell "${GPG}")' --keyring '$(escape_shell "${TRUSTEDFILE}")' \"\$@\"" > "${GPGHOMEDIR}/gpg.1.sh"
+ GPG="${GPGHOMEDIR}/gpg.1.sh"
+ fi
+}
+
+create_new_keyring() {
+ # gpg defaults to mode 0600 for new keyrings. Create one with 0644 instead.
+ if ! [ -e "$1" ]; then
+ if [ -w "$(dirname "$1")" ]; then
+ touch -- "$1"
+ chmod 0644 -- "$1"
+ fi
+ fi
+}
+
+aptkey_execute() { sh "$@"; }
+
+usage() {
+ echo "Usage: apt-key [--keyring file] [command] [arguments]"
+ echo
+ echo "Manage apt's list of trusted keys"
+ echo
+ echo " apt-key add <file> - add the key contained in <file> ('-' for stdin)"
+ echo " apt-key del <keyid> - remove the key <keyid>"
+ echo " apt-key export <keyid> - output the key <keyid>"
+ echo " apt-key exportall - output all trusted keys"
+ echo " apt-key update - update keys using the keyring package"
+ echo " apt-key net-update - update keys using the network"
+ echo " apt-key list - list keys"
+ echo " apt-key finger - list fingerprints"
+ echo " apt-key adv - pass advanced options to gpg (download key)"
+ echo
+ echo "If no specific keyring file is given the command applies to all keyring files."
+}
+
+while [ -n "$1" ]; do
+ case "$1" in
+ --keyring)
+ shift
+ if [ -z "$FORCED_KEYRING" -o "$FORCED_KEYRING" = '/dev/null' ]; then
+ TRUSTEDFILE="$1"
+ FORCED_KEYRING="$1"
+ elif [ "$TRUSTEDFILE" = "$FORCED_KEYRING" ]; then
+ create_gpg_home
+ FORCED_KEYRING="${GPGHOMEDIR}/mergedkeyrings.gpg"
+ echo -n '' > "$FORCED_KEYRING"
+ chmod 0644 -- "$FORCED_KEYRING"
+ catfile "$TRUSTEDFILE" "$FORCED_KEYRING"
+ catfile "$1" "$FORCED_KEYRING"
+ else
+ catfile "$1" "$FORCED_KEYRING"
+ fi
+ ;;
+ --keyid)
+ shift
+ if [ -n "$FORCED_KEYID" ]; then
+ apt_error 'Specifying --keyid multiple times is not supported'
+ exit 1
+ fi
+ FORCED_KEYID="$1"
+ ;;
+ --secret-keyring)
+ shift
+ FORCED_SECRET_KEYRING="$1"
+ ;;
+ --readonly)
+ merge_back_changes() { true; }
+ create_new_keyring() { if [ ! -r "$FORCED_KEYRING" ]; then TRUSTEDFILE='/dev/null'; FORCED_KEYRING="$TRUSTEDFILE"; fi; }
+ ;;
+ --fakeroot)
+ requires_root() { true; }
+ ;;
+ --quiet)
+ aptkey_echo() { true; }
+ ;;
+ --debug1)
+ # some cmds like finger redirect stderr to /dev/null …
+ aptkey_execute() { echo 'EXEC:' "$@"; sh "$@"; }
+ ;;
+ --debug2)
+ # … other more complicated ones pipe gpg into gpg.
+ aptkey_execute() { echo >&2 'EXEC:' "$@"; sh "$@"; }
+ ;;
+ --homedir)
+ # force usage of a specific homedir instead of creating a temporary
+ shift
+ GPGHOMEDIR="$1"
+ ;;
+ --*)
+ echo >&2 "Unknown option: $1"
+ usage
+ exit 1;;
+ *)
+ break;;
+ esac
+ shift
+done
+
+if [ -z "$TRUSTEDFILE" ]; then
+ TRUSTEDFILE="/etc/apt/trusted.gpg"
+ eval $(apt-config shell TRUSTEDFILE Apt::GPGV::TrustedKeyring)
+ eval $(apt-config shell TRUSTEDFILE Dir::Etc::Trusted/f)
+ if [ "$APT_KEY_NO_LEGACY_KEYRING" ]; then
+ TRUSTEDFILE="/dev/null"
+ fi
+fi
+
+command="$1"
+if [ -z "$command" ]; then
+ usage
+ exit 1
+fi
+shift
+
+prepare_gpg_home() {
+ # crude detection if we are called from a maintainerscript where the
+ # package depends on gnupg or not. We accept recommends here as
+ # well as the script hopefully uses apt-key optionally then like e.g.
+ # debian-archive-keyring for (upgrade) cleanup did
+ if [ -n "$DPKG_MAINTSCRIPT_PACKAGE" ] && [ -z "$APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE" ]; then
+ if ! dpkg-query --show --showformat '${Pre-Depends}${Depends}${Recommends}\n' "$DPKG_MAINTSCRIPT_PACKAGE" 2>/dev/null | grep -E -q 'gpg|gnupg'; then
+ cat >&2 <<EOF
+Warning: The $DPKG_MAINTSCRIPT_NAME maintainerscript of the package $DPKG_MAINTSCRIPT_PACKAGE
+Warning: seems to use apt-key (provided by apt) without depending on gpg, gnupg, or gnupg2.
+Warning: This will BREAK in the future and should be fixed by the package maintainer(s).
+Note: Check first if apt-key functionality is needed at all - it probably isn't!
+EOF
+ fi
+ fi
+ eval "$(apt-config shell GPG_EXE Apt::Key::gpgcommand)"
+ if [ -n "$GPG_EXE" ] && command_available "$GPG_EXE"; then
+ true
+ elif command_available 'gpg'; then
+ GPG_EXE="gpg"
+ elif command_available 'gpg2'; then
+ GPG_EXE="gpg2"
+ elif command_available 'gpg1'; then
+ GPG_EXE="gpg1"
+ else
+ apt_error 'gnupg, gnupg2 and gnupg1 do not seem to be installed, but one of them is required for this operation'
+ exit 255
+ fi
+
+ create_gpg_home
+
+ # now tell gpg that it shouldn't try to maintain this trustdb file
+ echo "#!/bin/sh
+exec '$(escape_shell "${GPG_EXE}")' --ignore-time-conflict --no-options --no-default-keyring \\
+--homedir '$(escape_shell "${GPGHOMEDIR}")' --no-auto-check-trustdb --trust-model always \"\$@\"" > "${GPGHOMEDIR}/gpg.0.sh"
+ GPG_SH="${GPGHOMEDIR}/gpg.0.sh"
+ GPG="$GPG_SH"
+
+ # create the trustdb with an (empty) dummy keyring
+ # older gpgs required it, newer gpgs even warn that it isn't needed,
+ # but require it nonetheless for some commands, so we just play safe
+ # here for the foreseeable future and create a dummy one
+ touch "${GPGHOMEDIR}/empty.gpg"
+ if ! "$GPG_EXE" --ignore-time-conflict --no-options --no-default-keyring \
+ --homedir "$GPGHOMEDIR" --quiet --check-trustdb --keyring "${GPGHOMEDIR}/empty.gpg" >"${GPGHOMEDIR}/gpgoutput.log" 2>&1; then
+ cat >&2 "${GPGHOMEDIR}/gpgoutput.log"
+ false
+ fi
+
+ # We don't usually need a secret keyring, of course, but
+ # for advanced operations, we might really need a secret keyring after all
+ if [ -n "$FORCED_SECRET_KEYRING" ] && [ -r "$FORCED_SECRET_KEYRING" ]; then
+ if ! aptkey_execute "$GPG" -v --batch --import "$FORCED_SECRET_KEYRING" >"${GPGHOMEDIR}/gpgoutput.log" 2>&1; then
+ # already imported keys cause gpg1 to fail for some reason… ignore this error
+ if ! grep -q 'already in secret keyring' "${GPGHOMEDIR}/gpgoutput.log"; then
+ cat >&2 "${GPGHOMEDIR}/gpgoutput.log"
+ false
+ fi
+ fi
+ else
+ # and then, there are older versions of gpg which panic and implode
+ # if there isn't one available - and writeable for imports
+ # and even if not output is littered with the creation of a secring,
+ # so lets call import once to have it create what it wants in silence
+ echo -n | aptkey_execute "$GPG" --batch --import >/dev/null 2>&1 || true
+ fi
+}
+
+warn_on_script_usage() {
+ if [ -n "$APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE" ]; then
+ return
+ fi
+ # (Maintainer) scripts should not be using apt-key
+ if [ -n "$DPKG_MAINTSCRIPT_PACKAGE" ]; then
+ echo >&2 "Warning: apt-key should not be used in scripts (called from $DPKG_MAINTSCRIPT_NAME maintainerscript of the package ${DPKG_MAINTSCRIPT_PACKAGE})"
+ fi
+
+ echo >&2 "Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8))."
+}
+
+warn_outside_maintscript() {
+ # In del, we want to warn in interactive use, but not inside maintainer
+ # scripts, so as to give people a chance to migrate keyrings.
+ #
+ # FIXME: We should always warn starting in 2022.
+ if [ -z "$DPKG_MAINTSCRIPT_PACKAGE" ]; then
+ echo >&2 "Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8))."
+ fi
+}
+
+if [ "$command" != 'help' ] && [ "$command" != 'verify' ]; then
+ prepare_gpg_home
+fi
+
+case "$command" in
+ add)
+ warn_on_script_usage
+ requires_root
+ setup_merged_keyring
+ aptkey_execute "$GPG" --quiet --batch --import "$@"
+ merge_back_changes
+ aptkey_echo "OK"
+ ;;
+ del|rm|remove)
+ # no script warning here as removing 'add' usage needs 'del' for cleanup
+ warn_outside_maintscript
+ requires_root
+ foreach_keyring_do 'remove_key_from_keyring' "$@"
+ aptkey_echo "OK"
+ ;;
+ update)
+ warn_on_script_usage
+ requires_root
+ setup_merged_keyring
+ update
+ merge_back_changes
+ ;;
+ net-update)
+ warn_on_script_usage
+ requires_root
+ setup_merged_keyring
+ net_update
+ merge_back_changes
+ ;;
+ list|finger*)
+ warn_on_script_usage
+ foreach_keyring_do 'list_keys_in_keyring' --fingerprint "$@"
+ ;;
+ export|exportall)
+ warn_on_script_usage
+ merge_all_trusted_keyrings_into_pubring
+ aptkey_execute "$GPG_SH" --keyring "${GPGHOMEDIR}/pubring.gpg" --armor --export "$@"
+ ;;
+ adv*)
+ warn_on_script_usage
+ setup_merged_keyring
+ aptkey_echo "Executing: $GPG" "$@"
+ aptkey_execute "$GPG" "$@"
+ merge_back_changes
+ ;;
+ verify)
+ GPGV=''
+ eval $(apt-config shell GPGV Apt::Key::gpgvcommand)
+ if [ -n "$GPGV" ] && command_available "$GPGV"; then true;
+ elif command_available 'gpgv'; then GPGV='gpgv';
+ elif command_available 'gpgv2'; then GPGV='gpgv2';
+ elif command_available 'gpgv1'; then GPGV='gpgv1';
+ else
+ apt_error 'gpgv, gpgv2 or gpgv1 required for verification, but neither seems installed'
+ exit 29
+ fi
+ # for a forced keyid we need gpg --export, so full wrapping required
+ if [ -n "$FORCED_KEYID" ]; then
+ prepare_gpg_home
+ else
+ create_gpg_home
+ fi
+ setup_merged_keyring
+ if [ -n "$FORCED_KEYRING" ]; then
+ "$GPGV" --homedir "${GPGHOMEDIR}" --keyring "$(dearmor_filename "${FORCED_KEYRING}")" --ignore-time-conflict "$@"
+ else
+ "$GPGV" --homedir "${GPGHOMEDIR}" --keyring "${GPGHOMEDIR}/pubring.gpg" --ignore-time-conflict "$@"
+ fi
+ ;;
+ help)
+ usage
+ ;;
+ *)
+ usage
+ exit 1
+ ;;
+esac
diff --git a/cmdline/apt-mark.cc b/cmdline/apt-mark.cc
new file mode 100644
index 0000000..3f3b008
--- /dev/null
+++ b/cmdline/apt-mark.cc
@@ -0,0 +1,476 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* #####################################################################
+ apt-mark - show and change auto-installed bit information
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/cacheset.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/depcache.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/init.h>
+#include <apt-pkg/macros.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/pkgsystem.h>
+#include <apt-pkg/statechanges.h>
+#include <apt-pkg/strutl.h>
+
+#include <apt-private/private-cmndline.h>
+#include <apt-private/private-main.h>
+#include <apt-private/private-output.h>
+
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <apti18n.h>
+ /*}}}*/
+using namespace std;
+
+/* DoAuto - mark packages as automatically/manually installed {{{*/
+static bool DoAuto(CommandLine &CmdL)
+{
+ pkgCacheFile CacheFile;
+ CacheFile.InhibitActionGroups(true);
+ pkgDepCache * const DepCache = CacheFile.GetDepCache();
+ if (unlikely(DepCache == nullptr))
+ return false;
+
+ APT::PackageList pkgset = APT::PackageList::FromCommandLine(CacheFile, CmdL.FileList + 1);
+ if (pkgset.empty() == true)
+ return _error->Error(_("No packages found"));
+
+ bool MarkAuto = strcasecmp(CmdL.FileList[0],"auto") == 0;
+
+ vector<string> PackagesMarked;
+
+ for (APT::PackageList::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg)
+ {
+ if (Pkg->CurrentVer == 0)
+ {
+ ioprintf(c1out,_("%s can not be marked as it is not installed.\n"), Pkg.FullName(true).c_str());
+ continue;
+ }
+ else if ((((*DepCache)[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto) == MarkAuto)
+ {
+ if (MarkAuto == false)
+ ioprintf(c1out,_("%s was already set to manually installed.\n"), Pkg.FullName(true).c_str());
+ else
+ ioprintf(c1out,_("%s was already set to automatically installed.\n"), Pkg.FullName(true).c_str());
+ continue;
+ }
+
+ PackagesMarked.push_back(Pkg.FullName(true));
+ DepCache->MarkAuto(Pkg, MarkAuto);
+ }
+
+ bool MarkWritten = false;
+ bool IsSimulation = _config->FindB("APT::Mark::Simulate", false);
+ if (PackagesMarked.size() > 0 && !IsSimulation) {
+ MarkWritten = DepCache->writeStateFile(NULL);
+ if(!MarkWritten) {
+ return MarkWritten;
+ }
+ }
+
+ if(IsSimulation || MarkWritten) {
+ for (vector<string>::const_iterator I = PackagesMarked.begin(); I != PackagesMarked.end(); ++I) {
+ if (MarkAuto == false)
+ ioprintf(c1out,_("%s set to manually installed.\n"), (*I).c_str());
+ else
+ ioprintf(c1out,_("%s set to automatically installed.\n"), (*I).c_str());
+ }
+ }
+ return true;
+}
+ /*}}}*/
+/* DoMarkAuto - mark packages as automatically/manually installed {{{*/
+/* Does the same as DoAuto but tries to do it exactly the same why as
+ the python implementation did it so it can be a drop-in replacement */
+static bool DoMarkAuto(CommandLine &CmdL)
+{
+ pkgCacheFile CacheFile;
+ CacheFile.InhibitActionGroups(true);
+ pkgDepCache * const DepCache = CacheFile.GetDepCache();
+ if (unlikely(DepCache == nullptr))
+ return false;
+
+ APT::PackageList pkgset = APT::PackageList::FromCommandLine(CacheFile, CmdL.FileList + 1);
+ if (pkgset.empty() == true)
+ return _error->Error(_("No packages found"));
+
+ bool const MarkAuto = strcasecmp(CmdL.FileList[0],"markauto") == 0;
+ bool const Verbose = _config->FindB("APT::MarkAuto::Verbose", false);
+ int AutoMarkChanged = 0;
+
+ for (APT::PackageList::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg)
+ {
+ if (Pkg->CurrentVer == 0 ||
+ (((*DepCache)[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto) == MarkAuto)
+ continue;
+
+ if (Verbose == true)
+ ioprintf(c1out, "changing %s to %d\n", Pkg.Name(), (MarkAuto == false) ? 0 : 1);
+
+ DepCache->MarkAuto(Pkg, MarkAuto);
+ ++AutoMarkChanged;
+ }
+ if (AutoMarkChanged > 0 && _config->FindB("APT::Mark::Simulate", false) == false)
+ return DepCache->writeStateFile(NULL);
+
+ _error->Notice(_("This command is deprecated. Please use 'apt-mark auto' and 'apt-mark manual' instead."));
+
+ return true;
+}
+ /*}}}*/
+// helper for Install-Recommends-Sections and Never-MarkAuto-Sections /*{{{*/
+// FIXME: Copied verbatim from apt-pkg/depcache.cc
+static bool ConfigValueInSubTree(const char* SubTree, std::string_view const needle)
+{
+ if (needle.empty())
+ return false;
+ Configuration::Item const *Opts = _config->Tree(SubTree);
+ if (Opts != nullptr && Opts->Child != nullptr)
+ {
+ Opts = Opts->Child;
+ for (; Opts != nullptr; Opts = Opts->Next)
+ {
+ if (Opts->Value.empty())
+ continue;
+ if (needle == Opts->Value)
+ return true;
+ }
+ }
+ return false;
+}
+static bool SectionInSubTree(char const * const SubTree, std::string_view Needle)
+{
+ if (ConfigValueInSubTree(SubTree, Needle))
+ return true;
+ auto const sub = Needle.rfind('/');
+ if (sub == std::string_view::npos)
+ {
+ std::string special{"/"};
+ special.append(Needle);
+ return ConfigValueInSubTree(SubTree, special);
+ }
+ return ConfigValueInSubTree(SubTree, Needle.substr(sub + 1));
+}
+ /*}}}*/
+/* DoMinimize - minimize manually installed {{{*/
+/* Traverses dependencies of meta packages and marks them as manually
+ * installed. */
+static bool DoMinimize(CommandLine &CmdL)
+{
+
+ pkgCacheFile CacheFile;
+ pkgDepCache *const DepCache = CacheFile.GetDepCache();
+ if (unlikely(DepCache == nullptr))
+ return false;
+
+ if (CmdL.FileList[1] != nullptr)
+ return _error->Error(_("%s does not take any arguments"), "apt-mark minimize-manual");
+
+ auto Debug = _config->FindB("Debug::AptMark::Minimize", false);
+ auto is_root = [&DepCache](pkgCache::PkgIterator const &pkg) {
+ auto ver = pkg.CurrentVer();
+ return ver.end() == false && ((*DepCache)[pkg].Flags & pkgCache::Flag::Auto) == 0 &&
+ ver->Section != 0 &&
+ SectionInSubTree("APT::Never-MarkAuto-Sections", ver.Section());
+ };
+
+ APT::PackageSet roots;
+ for (auto Pkg = DepCache->PkgBegin(); Pkg.end() == false; ++Pkg)
+ {
+ if (is_root(Pkg))
+ {
+ if (Debug)
+ std::clog << "Found root " << Pkg.Name() << std::endl;
+ roots.insert(Pkg);
+ }
+ }
+
+ APT::PackageSet workset(roots);
+ APT::PackageSet seen;
+ APT::PackageSet changed;
+
+ pkgDepCache::ActionGroup group(*DepCache);
+
+ while (workset.empty() == false)
+ {
+ if (Debug)
+ std::clog << "Iteration\n";
+
+ APT::PackageSet workset2;
+ for (auto &Pkg : workset)
+ {
+ if (seen.find(Pkg) != seen.end())
+ continue;
+
+ seen.insert(Pkg);
+
+ if (Debug)
+ std::cerr << " Visiting " << Pkg.FullName(true) << "\n";
+ if (roots.find(Pkg) == roots.end() && ((*DepCache)[Pkg].Flags & pkgCache::Flag::Auto) == 0)
+ {
+ DepCache->MarkAuto(Pkg, true);
+ changed.insert(Pkg);
+ }
+
+ // Visit dependencies, add them to next working set
+ for (auto Dep = Pkg.CurrentVer().DependsList(); !Dep.end(); ++Dep)
+ {
+ if (DepCache->IsImportantDep(Dep) == false)
+ continue;
+ std::unique_ptr<pkgCache::Version *[]> targets(Dep.AllTargets());
+ for (int i = 0; targets[i] != nullptr; i++)
+ {
+ pkgCache::VerIterator Tgt(*DepCache, targets[i]);
+ if (Tgt.ParentPkg()->CurrentVer != 0 && Tgt.ParentPkg().CurrentVer()->ID == Tgt->ID)
+ workset2.insert(Tgt.ParentPkg());
+ }
+ }
+ }
+
+ workset = std::move(workset2);
+ }
+
+ if (changed.empty()) {
+ cout << _("No changes necessary") << endl;
+ return true;
+ }
+
+ ShowList(c1out, _("The following packages will be marked as automatically installed:"), changed,
+ [](const pkgCache::PkgIterator &) { return true; },
+ &PrettyFullName,
+ &PrettyFullName);
+
+ if (_config->FindB("APT::Mark::Simulate", false) == false)
+ {
+ if (YnPrompt(_("Do you want to continue?"), false) == false)
+ return true;
+
+ return DepCache->writeStateFile(NULL);
+ }
+
+ return true;
+}
+ /*}}}*/
+
+/* ShowAuto - show automatically installed packages (sorted) {{{*/
+static bool ShowAuto(CommandLine &CmdL)
+{
+ pkgCacheFile CacheFile;
+ CacheFile.InhibitActionGroups(true);
+ pkgDepCache * const DepCache = CacheFile.GetDepCache();
+ if (unlikely(DepCache == nullptr))
+ return false;
+
+ std::vector<string> packages;
+
+ bool const ShowAuto = strcasecmp(CmdL.FileList[0],"showauto") == 0;
+
+ if (CmdL.FileList[1] == 0)
+ {
+ packages.reserve(DepCache->Head().PackageCount / 3);
+ for (pkgCache::PkgIterator P = DepCache->PkgBegin(); P.end() == false; ++P)
+ if (P->CurrentVer != 0 &&
+ (((*DepCache)[P].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto) == ShowAuto)
+ packages.push_back(P.FullName(true));
+ }
+ else
+ {
+ APT::CacheSetHelper helper(false); // do not show errors
+ 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->CurrentVer != 0 &&
+ (((*DepCache)[P].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto) == ShowAuto)
+ packages.push_back(P.FullName(true));
+ }
+
+ std::sort(packages.begin(), packages.end());
+
+ for (vector<string>::const_iterator I = packages.begin(); I != packages.end(); ++I)
+ std::cout << *I << std::endl;
+
+ return true;
+}
+ /*}}}*/
+// DoSelection - wrapping around dpkg selections /*{{{*/
+static bool DoSelection(CommandLine &CmdL)
+{
+ pkgCacheFile CacheFile;
+ CacheFile.InhibitActionGroups(true);
+ pkgCache * const Cache = CacheFile.GetPkgCache();
+ if (unlikely(Cache == nullptr))
+ return false;
+
+ APT::VersionVector pkgset = APT::VersionVector::FromCommandLine(CacheFile, CmdL.FileList + 1, APT::CacheSetHelper::INSTCAND);
+ if (pkgset.empty() == true)
+ return _error->Error(_("No packages found"));
+
+ APT::StateChanges marks;
+ if (strcasecmp(CmdL.FileList[0], "hold") == 0 || strcasecmp(CmdL.FileList[0], "unhold") == 0)
+ {
+ 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 on hold.\n"), V.ParentPkg().FullName(true).c_str());
+ });
+
+ if (doneBegin == pkgset.begin() && doneEnd == pkgset.end())
+ return true;
+
+ 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()));
+ }
+ else
+ {
+ // 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);
+ }
+ pkgset.clear();
+
+ bool success = true;
+ if (_config->FindB("APT::Mark::Simulate", false) == false)
+ {
+ success = marks.Save();
+ if (success == false)
+ _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;
+}
+ /*}}}*/
+static bool ShowSelection(CommandLine &CmdL) /*{{{*/
+{
+ pkgCacheFile CacheFile;
+ pkgCache * const Cache = CacheFile.GetPkgCache();
+ if (unlikely(Cache == nullptr))
+ 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 || strncasecmp(CmdL.FileList[0], "showheld", strlen("showheld")) == 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 == selector)
+ packages.push_back(P.FullName(true));
+ }
+ else
+ {
+ APT::CacheSetHelper helper(false); // do not show errors
+ 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 == selector)
+ packages.push_back(P.FullName(true));
+ }
+
+ std::sort(packages.begin(), packages.end());
+
+ for (vector<string>::const_iterator I = packages.begin(); I != packages.end(); ++I)
+ std::cout << *I << std::endl;
+
+ return true;
+}
+ /*}}}*/
+static bool ShowHelp(CommandLine &) /*{{{*/
+{
+ 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 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")},
+ {"minimize-manual", &DoMinimize, _("Mark all dependencies of meta packages as automatically 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 packages on hold")}, {"showholds",&ShowSelection, nullptr}, {"showheld",&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 CmdL;
+ auto const Cmds = ParseCommandLine(CmdL, APT_CMD::APT_MARK, &_config, &_system, argc, argv, &ShowHelp, &GetCommands);
+
+ InitOutput();
+
+ return DispatchCommandLine(CmdL, Cmds);
+}
+ /*}}}*/
diff --git a/cmdline/apt-report-mirror-failure b/cmdline/apt-report-mirror-failure
new file mode 100755
index 0000000..7c39064
--- /dev/null
+++ b/cmdline/apt-report-mirror-failure
@@ -0,0 +1,29 @@
+#!/usr/bin/python
+#
+# This is a stub that is meant to support failure reporting of
+# mirrors to a central database
+#
+# its currently not used
+
+import sys
+import urllib
+import apt_pkg
+
+apt_pkg.init()
+url = apt_pkg.Config.find("Acquire::Mirror::ReportFailures", "")
+ #"http://people.ubuntu.com:9000/mirror-failure")
+ #"http://localhost:9000/mirror-failure")
+if not url:
+ sys.exit(0)
+
+print "Reporting mirror failure to '%s'" % url
+
+data = {}
+data['mirror'] = sys.argv[1]
+data['failurl'] = sys.argv[2]
+data['error'] = sys.argv[3]
+f = urllib.urlopen(url, urllib.urlencode(data))
+f.read()
+f.close()
+
+
diff --git a/cmdline/apt-sortpkgs.cc b/cmdline/apt-sortpkgs.cc
new file mode 100644
index 0000000..5198254
--- /dev/null
+++ b/cmdline/apt-sortpkgs.cc
@@ -0,0 +1,166 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ APT Sort Packages - Program to sort Package and Source files
+
+ This program is quite simple, it just sorts the package files by
+ package and sorts the fields inside by the internal APT sort order.
+ Input is taken from a named file and sent to stdout.
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/init.h>
+#include <apt-pkg/pkgsystem.h>
+#include <apt-pkg/strutl.h>
+#include <apt-pkg/tagfile-keys.h>
+#include <apt-pkg/tagfile.h>
+
+#include <apt-private/private-cmndline.h>
+#include <apt-private/private-main.h>
+
+#include <algorithm>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+using namespace std;
+
+struct PkgName /*{{{*/
+{
+ string Name;
+ string Ver;
+ string Arch;
+ unsigned long Offset;
+ unsigned long Length;
+
+ inline int Compare3(const PkgName &x) const
+ {
+ int A = stringcasecmp(Name,x.Name);
+ if (A == 0)
+ {
+ A = stringcasecmp(Ver,x.Ver);
+ if (A == 0)
+ A = stringcasecmp(Arch,x.Arch);
+ }
+ return A;
+ }
+
+ bool operator <(const PkgName &x) const {return Compare3(x) < 0;};
+ bool operator >(const PkgName &x) const {return Compare3(x) > 0;};
+ bool operator ==(const PkgName &x) const {return Compare3(x) == 0;};
+};
+ /*}}}*/
+// DoIt - Sort a single file /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+static bool DoIt(string InFile)
+{
+ FileFd Fd(InFile,FileFd::ReadOnly);
+ pkgTagFile Tags(&Fd);
+ if (_error->PendingError() == true)
+ return false;
+
+ // Parse.
+ vector<PkgName> List;
+ pkgTagSection Section;
+ unsigned long Largest = 0;
+ unsigned long Offset = Tags.Offset();
+ bool Source = _config->FindB("APT::SortPkgs::Source",false);
+ while (Tags.Step(Section) == true)
+ {
+ PkgName Tmp;
+
+ /* Fetch the name, auto-detecting if this is a source file or a
+ package file */
+ Tmp.Name = Section.Find(pkgTagSection::Key::Package).to_string();
+ Tmp.Ver = Section.Find(pkgTagSection::Key::Version).to_string();
+ Tmp.Arch = Section.Find(pkgTagSection::Key::Architecture).to_string();
+
+ if (Tmp.Name.empty() == true)
+ return _error->Error(_("Unknown package record!"));
+
+ Tmp.Offset = Offset;
+ Tmp.Length = Section.size();
+ if (Largest < Tmp.Length)
+ Largest = Tmp.Length;
+
+ List.push_back(Tmp);
+
+ Offset = Tags.Offset();
+ }
+ if (_error->PendingError() == true)
+ return false;
+
+ // Sort it
+ sort(List.begin(),List.end());
+
+ const char **Order = TFRewritePackageOrder;
+ if (Source == true)
+ Order = TFRewriteSourceOrder;
+
+ // Emit
+ FileFd stdoutfd;
+ stdoutfd.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly, false);
+ auto const Buffer = std::unique_ptr<unsigned char[]>(new unsigned char[Largest+1]);
+ for (vector<PkgName>::iterator I = List.begin(); I != List.end(); ++I)
+ {
+ // Read in the Record.
+ if (Fd.Seek(I->Offset) == false || Fd.Read(Buffer.get(),I->Length) == false)
+ return false;
+
+ Buffer[I->Length] = '\n';
+ if (Section.Scan((char *)Buffer.get(),I->Length+1) == false)
+ return _error->Error("Internal error, failed to scan buffer");
+
+ // Sort the section
+ if (Section.Write(stdoutfd, Order) == false || stdoutfd.Write("\n", 1) == false)
+ return _error->Error("Internal error, failed to sort fields");
+ }
+ return true;
+}
+ /*}}}*/
+static bool ShowHelp(CommandLine &) /*{{{*/
+{
+ std::cout <<
+ _("Usage: apt-sortpkgs [options] file1 [file2 ...]\n"
+ "\n"
+ "apt-sortpkgs is a simple tool to sort package information files.\n"
+ "By default it sorts by binary package information, but the -s option\n"
+ "can be used to switch to source package ordering instead.\n");
+ return true;
+}
+ /*}}}*/
+static std::vector<aptDispatchWithHelp> GetCommands() /*{{{*/
+{
+ return {
+ {nullptr, nullptr, nullptr}
+ };
+}
+ /*}}}*/
+int main(int argc,const char *argv[]) /*{{{*/
+{
+ CommandLine CmdL;
+ ParseCommandLine(CmdL, APT_CMD::APT_SORTPKG, &_config, &_system, argc, argv, &ShowHelp, &GetCommands);
+
+ // Match the operation
+ for (unsigned int I = 0; I != CmdL.FileSize(); I++)
+ if (DoIt(CmdL.FileList[I]) == false)
+ break;
+
+ return DispatchCommandLine(CmdL, {});
+}
+ /*}}}*/
diff --git a/cmdline/apt.cc b/cmdline/apt.cc
new file mode 100644
index 0000000..f53d4e4
--- /dev/null
+++ b/cmdline/apt.cc
@@ -0,0 +1,123 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ apt - CLI UI for apt
+
+ Returns 100 on failure, 0 on success.
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/init.h>
+#include <apt-pkg/pkgsystem.h>
+#include <apt-pkg/strutl.h>
+
+#include <apt-private/private-cmndline.h>
+#include <apt-private/private-depends.h>
+#include <apt-private/private-download.h>
+#include <apt-private/private-install.h>
+#include <apt-private/private-list.h>
+#include <apt-private/private-main.h>
+#include <apt-private/private-moo.h>
+#include <apt-private/private-output.h>
+#include <apt-private/private-search.h>
+#include <apt-private/private-show.h>
+#include <apt-private/private-source.h>
+#include <apt-private/private-sources.h>
+#include <apt-private/private-update.h>
+#include <apt-private/private-upgrade.h>
+
+#include <iostream>
+#include <vector>
+#include <unistd.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+static bool ShowHelp(CommandLine &) /*{{{*/
+{
+ std::cout <<
+ _("Usage: apt [options] command\n"
+ "\n"
+ "apt is a commandline package manager and provides commands for\n"
+ "searching and managing as well as querying information about packages.\n"
+ "It provides the same functionality as the specialized APT tools,\n"
+ "like apt-get and apt-cache, but enables options more suitable for\n"
+ "interactive use by default.\n");
+ return true;
+}
+ /*}}}*/
+static std::vector<aptDispatchWithHelp> GetCommands() /*{{{*/
+{
+ // advanced commands are left undocumented on purpose
+ return {
+ // query
+ {"list", &DoList, _("list packages based on package names")},
+ {"search", &DoSearch, _("search in package descriptions")},
+ {"show", &ShowPackage, _("show package details")},
+
+ // package stuff
+ {"install", &DoInstall, _("install packages")},
+ {"reinstall", &DoInstall, _("reinstall packages")},
+ {"remove", &DoInstall, _("remove packages")},
+ {"autoremove", &DoInstall, _("automatically remove all unused packages")},
+ {"auto-remove", &DoInstall, nullptr},
+ {"autopurge",&DoInstall, nullptr},
+ {"purge", &DoInstall, nullptr},
+
+ // system wide stuff
+ {"update", &DoUpdate, _("update list of available packages")},
+ {"upgrade", &DoUpgrade, _("upgrade the system by installing/upgrading packages")},
+ {"full-upgrade", &DoDistUpgrade, _("upgrade the system by removing/installing/upgrading packages")},
+
+ // misc
+ {"edit-sources", &EditSources, _("edit the source information file")},
+ {"moo", &DoMoo, nullptr},
+ {"satisfy", &DoBuildDep, _("satisfy dependency strings")},
+
+ // for compat with muscle memory
+ {"dist-upgrade", &DoDistUpgrade, nullptr},
+ {"showsrc",&ShowSrcPackage, nullptr},
+ {"depends",&Depends, nullptr},
+ {"rdepends",&RDepends, nullptr},
+ {"policy",&Policy, nullptr},
+ {"build-dep", &DoBuildDep,nullptr},
+ {"clean", &DoClean, nullptr},
+ {"autoclean", &DoAutoClean, nullptr},
+ {"auto-clean", &DoAutoClean, nullptr},
+ {"source", &DoSource, nullptr},
+ {"download", &DoDownload, nullptr},
+ {"changelog", &DoChangelog, nullptr},
+ {"info", &ShowPackage, nullptr},
+
+ {nullptr, nullptr, nullptr}
+ };
+}
+ /*}}}*/
+int main(int argc, const char *argv[]) /*{{{*/
+{
+ CommandLine CmdL;
+ auto const Cmds = ParseCommandLine(CmdL, APT_CMD::APT, &_config, &_system, argc, argv, &ShowHelp, &GetCommands);
+
+ int const quiet = _config->FindI("quiet", 0);
+ if (quiet == 2)
+ {
+ _config->CndSet("quiet::NoProgress", true);
+ _config->Set("quiet", 1);
+ }
+
+ InitSignals();
+ InitOutput();
+
+ CheckIfCalledByScript(argc, argv);
+ CheckIfSimulateMode(CmdL);
+
+ return DispatchCommandLine(CmdL, Cmds);
+}
+ /*}}}*/