// -*- 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 <cerrno>
#include <csignal>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <grp.h>
#include <pwd.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")));
	 if (not (*S)->GetSignedBy().empty())
	    AddOptions.insert(std::make_pair("SIGNED_BY", SubstVar(SubstVar(APT::String::Strip((*S)->GetSignedBy()), "\n\n", "\n.\n"), "\n", "\n ")));
	 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},
      {"distclean", &DoDistClean, nullptr},
      {"dist-clean", &DoDistClean, 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);
}
									/*}}}*/
