"Fossies" - the Fresh Open Source Software Archive

Member "apt-1.9.4/apt-pkg/sourcelist.cc" (19 Sep 2019, 22058 Bytes) of package /linux/misc/apt-1.9.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "sourcelist.cc" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.8.2_vs_1.9.2.

    1 // -*- mode: cpp; mode: fold -*-
    2 // Description                              /*{{{*/
    3 /* ######################################################################
    4 
    5    List of Sources
    6    
    7    ##################################################################### */
    8                                     /*}}}*/
    9 // Include Files                            /*{{{*/
   10 #include <config.h>
   11 
   12 #include <apt-pkg/cmndline.h>
   13 #include <apt-pkg/configuration.h>
   14 #include <apt-pkg/debindexfile.h>
   15 #include <apt-pkg/debsrcrecords.h>
   16 #include <apt-pkg/error.h>
   17 #include <apt-pkg/fileutl.h>
   18 #include <apt-pkg/indexfile.h>
   19 #include <apt-pkg/metaindex.h>
   20 #include <apt-pkg/pkgcache.h>
   21 #include <apt-pkg/sourcelist.h>
   22 #include <apt-pkg/strutl.h>
   23 #include <apt-pkg/tagfile.h>
   24 
   25 #include <algorithm>
   26 #include <cstring>
   27 #include <fstream>
   28 #include <map>
   29 #include <string>
   30 #include <vector>
   31 #include <ctype.h>
   32 #include <stddef.h>
   33 #include <time.h>
   34 
   35 #include <apti18n.h>
   36                                     /*}}}*/
   37 
   38 using namespace std;
   39 
   40 // Global list of Items supported
   41 static pkgSourceList::Type *ItmList[10];
   42 pkgSourceList::Type **pkgSourceList::Type::GlobalList = ItmList;
   43 unsigned long pkgSourceList::Type::GlobalListLen = 0;
   44 
   45 static std::vector<std::string> FindMultiValue(pkgTagSection &Tags, char const *const Field) /*{{{*/
   46 {
   47    auto values = Tags.FindS(Field);
   48    // we ignore duplicate spaces by removing empty values
   49    std::replace_if(values.begin(), values.end(), isspace_ascii, ' ');
   50    auto vect = VectorizeString(values, ' ');
   51    vect.erase(std::remove_if(vect.begin(), vect.end(), [](std::string const &s) { return s.empty(); }), vect.end());
   52    return vect;
   53 }
   54                                     /*}}}*/
   55 
   56 // Type::Type - Constructor                     /*{{{*/
   57 // ---------------------------------------------------------------------
   58 /* Link this to the global list of items*/
   59 pkgSourceList::Type::Type(char const * const pName, char const * const pLabel) : Name(pName), Label(pLabel)
   60 {
   61    ItmList[GlobalListLen] = this;
   62    ++GlobalListLen;
   63 }
   64 pkgSourceList::Type::~Type() {}
   65                                     /*}}}*/
   66 // Type::GetType - Get a specific meta for a given type         /*{{{*/
   67 // ---------------------------------------------------------------------
   68 /* */
   69 pkgSourceList::Type *pkgSourceList::Type::GetType(const char *Type)
   70 {
   71    for (unsigned I = 0; I != GlobalListLen; ++I)
   72       if (strcmp(GlobalList[I]->Name,Type) == 0)
   73      return GlobalList[I];
   74    return 0;
   75 }
   76                                     /*}}}*/
   77 // Type::FixupURI - Normalize the URI and check it..            /*{{{*/
   78 // ---------------------------------------------------------------------
   79 /* */
   80 bool pkgSourceList::Type::FixupURI(string &URI) const
   81 {
   82    if (URI.empty() == true)
   83       return false;
   84 
   85    if (URI.find(':') == string::npos)
   86       return false;
   87 
   88    URI = SubstVar(URI,"$(ARCH)",_config->Find("APT::Architecture"));
   89    
   90    // Make sure that the URI is / postfixed
   91    if (URI[URI.size() - 1] != '/')
   92       URI += '/';
   93    
   94    return true;
   95 }
   96                                     /*}}}*/
   97 bool pkgSourceList::Type::ParseStanza(vector<metaIndex *> &List,    /*{{{*/
   98                                       pkgTagSection &Tags,
   99                                       unsigned int const i,
  100                                       FileFd &Fd)
  101 {
  102    map<string, string> Options;
  103 
  104    string Enabled = Tags.FindS("Enabled");
  105    if (Enabled.empty() == false && StringToBool(Enabled) == false)
  106       return true;
  107 
  108    std::map<char const * const, std::pair<char const * const, bool> > mapping;
  109 #define APT_PLUSMINUS(X, Y) \
  110    mapping.insert(std::make_pair(X, std::make_pair(Y, true))); \
  111    mapping.insert(std::make_pair(X "-Add", std::make_pair(Y "+", true))); \
  112    mapping.insert(std::make_pair(X "-Remove", std::make_pair(Y "-", true)))
  113    APT_PLUSMINUS("Architectures", "arch");
  114    APT_PLUSMINUS("Languages", "lang");
  115    APT_PLUSMINUS("Targets", "target");
  116 #undef APT_PLUSMINUS
  117    mapping.insert(std::make_pair("Trusted", std::make_pair("trusted", false)));
  118    mapping.insert(std::make_pair("Check-Valid-Until", std::make_pair("check-valid-until", false)));
  119    mapping.insert(std::make_pair("Valid-Until-Min", std::make_pair("valid-until-min", false)));
  120    mapping.insert(std::make_pair("Valid-Until-Max", std::make_pair("valid-until-max", false)));
  121    mapping.insert(std::make_pair("Check-Date", std::make_pair("check-date", false)));
  122    mapping.insert(std::make_pair("Date-Max-Future", std::make_pair("date-max-future", false)));
  123    mapping.insert(std::make_pair("Signed-By", std::make_pair("signed-by", false)));
  124    mapping.insert(std::make_pair("PDiffs", std::make_pair("pdiffs", false)));
  125    mapping.insert(std::make_pair("By-Hash", std::make_pair("by-hash", false)));
  126 
  127    for (std::map<char const * const, std::pair<char const * const, bool> >::const_iterator m = mapping.begin(); m != mapping.end(); ++m)
  128       if (Tags.Exists(m->first))
  129       {
  130      if (m->second.second)
  131      {
  132         auto const values = FindMultiValue(Tags, m->first);
  133         Options[m->second.first] = APT::String::Join(values, ",");
  134      }
  135      else
  136         Options[m->second.first] = Tags.FindS(m->first);
  137       }
  138 
  139    {
  140       std::string entry;
  141       strprintf(entry, "%s:%i", Fd.Name().c_str(), i);
  142       Options["sourceslist-entry"] = entry;
  143    }
  144 
  145    // now create one item per suite/section
  146    auto const list_uris = FindMultiValue(Tags, "URIs");
  147    auto const list_comp = FindMultiValue(Tags, "Components");
  148    auto list_suite = FindMultiValue(Tags, "Suites");
  149    {
  150       auto const nativeArch = _config->Find("APT::Architecture");
  151       std::transform(list_suite.begin(), list_suite.end(), list_suite.begin(),
  152              [&](std::string const &suite) { return SubstVar(suite, "$(ARCH)", nativeArch); });
  153    }
  154 
  155    if (list_uris.empty())
  156       // TRANSLATOR: %u is a line number, the first %s is a filename of a file with the extension "second %s" and the third %s is a unique identifier for bugreports
  157       return _error->Error(_("Malformed entry %u in %s file %s (%s)"), i, "sources", Fd.Name().c_str(), "URI");
  158 
  159    if (list_suite.empty())
  160       return _error->Error(_("Malformed entry %u in %s file %s (%s)"), i, "sources", Fd.Name().c_str(), "Suite");
  161 
  162    for (auto URI : list_uris)
  163    {
  164       if (FixupURI(URI) == false)
  165      return _error->Error(_("Malformed entry %u in %s file %s (%s)"), i, "sources", Fd.Name().c_str(), "URI parse");
  166 
  167       for (auto const &S : list_suite)
  168       {
  169      if (likely(S.empty() == false) && S[S.size() - 1] == '/')
  170      {
  171         if (list_comp.empty() == false)
  172            return _error->Error(_("Malformed entry %u in %s file %s (%s)"), i, "sources", Fd.Name().c_str(), "absolute Suite Component");
  173         if (CreateItem(List, URI, S, "", Options) == false)
  174            return false;
  175      }
  176      else
  177      {
  178         if (list_comp.empty())
  179            return _error->Error(_("Malformed entry %u in %s file %s (%s)"), i, "sources", Fd.Name().c_str(), "Component");
  180 
  181         for (auto const &C : list_comp)
  182            if (CreateItem(List, URI, S, C, Options) == false)
  183           return false;
  184      }
  185       }
  186    }
  187    return true;
  188 }
  189                                     /*}}}*/
  190 // Type::ParseLine - Parse a single line                /*{{{*/
  191 // ---------------------------------------------------------------------
  192 /* This is a generic one that is the 'usual' format for sources.list
  193    Weird types may override this. */
  194 bool pkgSourceList::Type::ParseLine(vector<metaIndex *> &List,
  195                     const char *Buffer,
  196                     unsigned int const CurLine,
  197                     string const &File) const
  198 {
  199    for (;Buffer != 0 && isspace(*Buffer); ++Buffer); // Skip whitespaces
  200 
  201    // Parse option field if it exists
  202    // e.g.: [ option1=value1 option2=value2 ]
  203    map<string, string> Options;
  204    {
  205       std::string entry;
  206       strprintf(entry, "%s:%i", File.c_str(), CurLine);
  207       Options["sourceslist-entry"] = entry;
  208    }
  209    if (Buffer != 0 && Buffer[0] == '[')
  210    {
  211       ++Buffer; // ignore the [
  212       for (;Buffer != 0 && isspace(*Buffer); ++Buffer); // Skip whitespaces
  213       while (*Buffer != ']')
  214       {
  215      // get one option, e.g. option1=value1
  216      string option;
  217      if (ParseQuoteWord(Buffer,option) == false)
  218         return _error->Error(_("Malformed entry %u in %s file %s (%s)"), CurLine, "list", File.c_str(), "[option] unparsable");
  219 
  220      if (option.length() < 3)
  221         return _error->Error(_("Malformed entry %u in %s file %s (%s)"), CurLine, "list", File.c_str(), "[option] too short");
  222 
  223      // accept options even if the last has no space before the ]-end marker
  224      if (option.at(option.length()-1) == ']')
  225      {
  226         for (; *Buffer != ']'; --Buffer);
  227         option.resize(option.length()-1);
  228      }
  229 
  230      size_t const needle = option.find('=');
  231      if (needle == string::npos)
  232         return _error->Error(_("Malformed entry %u in %s file %s (%s)"), CurLine, "list", File.c_str(), "[option] not assignment");
  233 
  234      string const key = string(option, 0, needle);
  235      string const value = string(option, needle + 1, option.length());
  236 
  237      if (key.empty() == true)
  238         return _error->Error(_("Malformed entry %u in %s file %s (%s)"), CurLine, "list", File.c_str(), "[option] no key");
  239 
  240      if (value.empty() == true)
  241         return _error->Error(_("Malformed entry %u in %s file %s (%s)"), CurLine, "list", File.c_str(), "[option] no value");
  242 
  243      Options[key] = value;
  244       }
  245       ++Buffer; // ignore the ]
  246       for (;Buffer != 0 && isspace(*Buffer); ++Buffer); // Skip whitespaces
  247    }
  248 
  249    string URI;
  250    string Dist;
  251    string Section;
  252 
  253    if (ParseQuoteWord(Buffer,URI) == false)
  254       return _error->Error(_("Malformed entry %u in %s file %s (%s)"), CurLine, "list", File.c_str(), "URI");
  255    if (ParseQuoteWord(Buffer,Dist) == false)
  256       return _error->Error(_("Malformed entry %u in %s file %s (%s)"), CurLine, "list", File.c_str(), "Suite");
  257 
  258    if (FixupURI(URI) == false)
  259       return _error->Error(_("Malformed entry %u in %s file %s (%s)"), CurLine, "list", File.c_str(), "URI parse");
  260 
  261    // Check for an absolute dists specification.
  262    if (Dist.empty() == false && Dist[Dist.size() - 1] == '/')
  263    {
  264       if (ParseQuoteWord(Buffer,Section) == true)
  265      return _error->Error(_("Malformed entry %u in %s file %s (%s)"), CurLine, "list", File.c_str(), "absolute Suite Component");
  266       Dist = SubstVar(Dist,"$(ARCH)",_config->Find("APT::Architecture"));
  267       return CreateItem(List, URI, Dist, Section, Options);
  268    }
  269 
  270    // Grab the rest of the dists
  271    if (ParseQuoteWord(Buffer,Section) == false)
  272       return _error->Error(_("Malformed entry %u in %s file %s (%s)"), CurLine, "list", File.c_str(), "Component");
  273 
  274    do
  275    {
  276       if (CreateItem(List, URI, Dist, Section, Options) == false)
  277      return false;
  278    }
  279    while (ParseQuoteWord(Buffer,Section) == true);
  280 
  281    return true;
  282 }
  283                                     /*}}}*/
  284 // SourceList::pkgSourceList - Constructors             /*{{{*/
  285 // ---------------------------------------------------------------------
  286 /* */
  287 pkgSourceList::pkgSourceList() : d(NULL)
  288 {
  289 }
  290                                     /*}}}*/
  291 // SourceList::~pkgSourceList - Destructor              /*{{{*/
  292 // ---------------------------------------------------------------------
  293 /* */
  294 pkgSourceList::~pkgSourceList()
  295 {
  296    for (const_iterator I = SrcList.begin(); I != SrcList.end(); ++I)
  297       delete *I;
  298    SrcList.clear();
  299    for (auto  F = VolatileFiles.begin(); F != VolatileFiles.end(); ++F)
  300       delete (*F);
  301    VolatileFiles.clear();
  302 }
  303                                     /*}}}*/
  304 // SourceList::ReadMainList - Read the main source list from etc    /*{{{*/
  305 // ---------------------------------------------------------------------
  306 /* */
  307 bool pkgSourceList::ReadMainList()
  308 {
  309    Reset();
  310    // CNC:2003-11-28 - Entries in sources.list have priority over
  311    //                  entries in sources.list.d.
  312    string Main = _config->FindFile("Dir::Etc::sourcelist", "/dev/null");
  313    string Parts = _config->FindDir("Dir::Etc::sourceparts", "/dev/null");
  314 
  315    _error->PushToStack();
  316    if (RealFileExists(Main) == true)
  317       ReadAppend(Main);
  318    else if (DirectoryExists(Parts) == false && APT::String::Endswith(Parts, "/dev/null") == false)
  319       // Only warn if there are no sources.list.d.
  320       _error->WarningE("DirectoryExists", _("Unable to read %s"), Parts.c_str());
  321 
  322    if (DirectoryExists(Parts) == true)
  323       ReadSourceDir(Parts);
  324    else if (Main.empty() == false && RealFileExists(Main) == false &&
  325      APT::String::Endswith(Parts, "/dev/null") == false)
  326       // Only warn if there is no sources.list file.
  327       _error->WarningE("RealFileExists", _("Unable to read %s"), Main.c_str());
  328 
  329    for (auto && file: _config->FindVector("APT::Sources::With"))
  330       AddVolatileFile(file, nullptr);
  331 
  332    auto good = _error->PendingError() == false;
  333    _error->MergeWithStack();
  334    return good;
  335 }
  336                                     /*}}}*/
  337 // SourceList::Reset - Clear the sourcelist contents            /*{{{*/
  338 // ---------------------------------------------------------------------
  339 /* */
  340 void pkgSourceList::Reset()
  341 {
  342    for (const_iterator I = SrcList.begin(); I != SrcList.end(); ++I)
  343       delete *I;
  344    SrcList.clear();
  345 }
  346                                     /*}}}*/
  347 // SourceList::Read - Parse the sourcelist file             /*{{{*/
  348 // ---------------------------------------------------------------------
  349 /* */
  350 bool pkgSourceList::Read(string const &File)
  351 {
  352    Reset();
  353    return ReadAppend(File);
  354 }
  355                                     /*}}}*/
  356 // SourceList::ReadAppend - Parse a sourcelist file         /*{{{*/
  357 // ---------------------------------------------------------------------
  358 /* */
  359 bool pkgSourceList::ReadAppend(string const &File)
  360 {
  361    if (flExtension(File) == "sources")
  362       return ParseFileDeb822(File);
  363    else
  364       return ParseFileOldStyle(File);
  365 }
  366                                     /*}}}*/
  367 // SourceList::ReadFileOldStyle - Read Traditional style sources.list   /*{{{*/
  368 // ---------------------------------------------------------------------
  369 /* */
  370 bool pkgSourceList::ParseFileOldStyle(std::string const &File)
  371 {
  372    FileFd Fd;
  373    if (OpenConfigurationFileFd(File, Fd) == false)
  374       return false;
  375 
  376    std::string Buffer;
  377    for (unsigned int CurLine = 1; Fd.ReadLine(Buffer); ++CurLine)
  378    {
  379       // remove comments
  380       size_t curpos = 0;
  381       while ((curpos = Buffer.find('#', curpos)) != std::string::npos)
  382       {
  383      size_t const openbrackets = std::count(Buffer.begin(), Buffer.begin() + curpos, '[');
  384      size_t const closedbrackets = std::count(Buffer.begin(), Buffer.begin() + curpos, ']');
  385      if (openbrackets > closedbrackets)
  386      {
  387         // a # in an option, unlikely, but oh well, it was supported so stick to it
  388         ++curpos;
  389         continue;
  390      }
  391      Buffer.erase(curpos);
  392      break;
  393       }
  394       // remove spaces before/after
  395       curpos = Buffer.find_first_not_of(" \t\r");
  396       if (curpos != 0)
  397      Buffer.erase(0, curpos);
  398       curpos = Buffer.find_last_not_of(" \t\r");
  399       if (curpos != std::string::npos)
  400      Buffer.erase(curpos + 1);
  401 
  402       if (Buffer.empty())
  403      continue;
  404 
  405       // Grok it
  406       std::string const LineType = Buffer.substr(0, Buffer.find_first_of(" \t\v"));
  407       if (LineType.empty() || LineType == Buffer)
  408      return _error->Error(_("Malformed line %u in source list %s (type)"),CurLine,File.c_str());
  409 
  410       Type *Parse = Type::GetType(LineType.c_str());
  411       if (Parse == 0)
  412      return _error->Error(_("Type '%s' is not known on line %u in source list %s"),LineType.c_str(),CurLine,File.c_str());
  413 
  414       if (Parse->ParseLine(SrcList, Buffer.c_str() + LineType.length(), CurLine, File) == false)
  415      return false;
  416    }
  417    return true;
  418 }
  419                                     /*}}}*/
  420 // SourceList::ParseFileDeb822 - Parse deb822 style sources.list    /*{{{*/
  421 // ---------------------------------------------------------------------
  422 /* Returns: the number of stanzas parsed*/
  423 bool pkgSourceList::ParseFileDeb822(string const &File)
  424 {
  425    // see if we can read the file
  426    FileFd Fd;
  427    if (OpenConfigurationFileFd(File, Fd) == false)
  428       return false;
  429    pkgTagFile Sources(&Fd, pkgTagFile::SUPPORT_COMMENTS);
  430    if (Fd.IsOpen() == false || Fd.Failed())
  431       return _error->Error(_("Malformed stanza %u in source list %s (type)"),0,File.c_str());
  432 
  433    // read step by step
  434    pkgTagSection Tags;
  435    unsigned int i = 0;
  436    while (Sources.Step(Tags) == true)
  437    {
  438       ++i;
  439       if(Tags.Exists("Types") == false)
  440      return _error->Error(_("Malformed stanza %u in source list %s (type)"),i,File.c_str());
  441 
  442       for (auto const &type : FindMultiValue(Tags, "Types"))
  443       {
  444      Type *Parse = Type::GetType(type.c_str());
  445      if (Parse == 0)
  446      {
  447         _error->Error(_("Type '%s' is not known on stanza %u in source list %s"), type.c_str(), i, Fd.Name().c_str());
  448         return false;
  449      }
  450 
  451      if (!Parse->ParseStanza(SrcList, Tags, i, Fd))
  452         return false;
  453       }
  454    }
  455    return true;
  456 }
  457                                     /*}}}*/
  458 // SourceList::FindIndex - Get the index associated with a file     /*{{{*/
  459 static bool FindInIndexFileContainer(std::vector<pkgIndexFile *> const &Cont, pkgCache::PkgFileIterator const &File, pkgIndexFile *&Found)
  460 {
  461    auto const J = std::find_if(Cont.begin(), Cont.end(), [&File](pkgIndexFile const * const J) {
  462      return J->FindInCache(*File.Cache()) == File;
  463    });
  464    if (J != Cont.end())
  465    {
  466       Found = (*J);
  467       return true;
  468    }
  469    return false;
  470 }
  471 bool pkgSourceList::FindIndex(pkgCache::PkgFileIterator File,
  472                   pkgIndexFile *&Found) const
  473 {
  474    for (const_iterator I = SrcList.begin(); I != SrcList.end(); ++I)
  475       if (FindInIndexFileContainer(*(*I)->GetIndexFiles(), File, Found))
  476      return true;
  477 
  478    return FindInIndexFileContainer(VolatileFiles, File, Found);
  479 }
  480                                     /*}}}*/
  481 // SourceList::GetIndexes - Load the index files into the downloader    /*{{{*/
  482 // ---------------------------------------------------------------------
  483 /* */
  484 bool pkgSourceList::GetIndexes(pkgAcquire *Owner, bool GetAll) const
  485 {
  486    for (const_iterator I = SrcList.begin(); I != SrcList.end(); ++I)
  487       if ((*I)->GetIndexes(Owner,GetAll) == false)
  488      return false;
  489    return true;
  490 }
  491                                     /*}}}*/
  492 // CNC:2003-03-03 - By Anton V. Denisov <avd@altlinux.org>.
  493 // SourceList::ReadSourceDir - Read a directory with sources files
  494 // Based on ReadConfigDir()                     /*{{{*/
  495 // ---------------------------------------------------------------------
  496 /* */
  497 bool pkgSourceList::ReadSourceDir(string const &Dir)
  498 {
  499    std::vector<std::string> const ext = {"list", "sources"};
  500    // Read the files
  501    bool good = true;
  502    for (auto const &I : GetListOfFilesInDir(Dir, ext, true))
  503       good = ReadAppend(I) && good;
  504    return good;
  505 }
  506                                     /*}}}*/
  507 // GetLastModified()                        /*{{{*/
  508 // ---------------------------------------------------------------------
  509 /* */
  510 time_t pkgSourceList::GetLastModifiedTime()
  511 {
  512    vector<string> List;
  513 
  514    string Main = _config->FindFile("Dir::Etc::sourcelist");
  515    string Parts = _config->FindDir("Dir::Etc::sourceparts");
  516 
  517    // go over the parts
  518    if (DirectoryExists(Parts) == true)
  519       List = GetListOfFilesInDir(Parts, "list", true);
  520 
  521    // calculate the time
  522    std::vector<time_t> modtimes;
  523    modtimes.reserve(1 + List.size());
  524    modtimes.push_back(GetModificationTime(Main));
  525    std::transform(List.begin(), List.end(), std::back_inserter(modtimes), GetModificationTime);
  526    auto const maxmtime = std::max_element(modtimes.begin(), modtimes.end());
  527    return *maxmtime;
  528 }
  529                                     /*}}}*/
  530 std::vector<pkgIndexFile*> pkgSourceList::GetVolatileFiles() const  /*{{{*/
  531 {
  532    return VolatileFiles;
  533 }
  534                                     /*}}}*/
  535 void pkgSourceList::AddVolatileFile(pkgIndexFile * const File)      /*{{{*/
  536 {
  537    if (File != nullptr)
  538       VolatileFiles.push_back(File);
  539 }
  540                                     /*}}}*/
  541 static bool fileNameMatches(std::string const &filename, std::string const &idxtype)/*{{{*/
  542 {
  543    for (auto && type: APT::Configuration::getCompressionTypes())
  544    {
  545       if (type == "uncompressed")
  546       {
  547      if (filename == idxtype || APT::String::Endswith(filename, '_' + idxtype))
  548         return true;
  549       }
  550       else if (filename == idxtype + '.' + type ||
  551         APT::String::Endswith(filename, '_' + idxtype + '.' + type))
  552      return true;
  553    }
  554    return false;
  555 }
  556                                     /*}}}*/
  557 bool pkgSourceList::AddVolatileFile(std::string const &File, std::vector<std::string> * const VolatileCmdL)/*{{{*/
  558 {
  559    // Note: FileExists matches directories and links, too!
  560    if (File.empty() || FileExists(File) == false)
  561       return false;
  562 
  563    std::string const ext = flExtension(File);
  564    // udeb is not included as installing it is usually a mistake rather than intended
  565    if (ext == "deb" || ext == "ddeb")
  566       AddVolatileFile(new debDebPkgFileIndex(File));
  567    else if (ext == "dsc")
  568       AddVolatileFile(new debDscFileIndex(File));
  569    else if (FileExists(flCombine(File, "debian/control")))
  570       AddVolatileFile(new debDscFileIndex(flCombine(File, "debian/control")));
  571    else if (ext == "changes")
  572    {
  573       debDscRecordParser changes(File, nullptr);
  574       std::vector<pkgSrcRecords::File> fileslst;
  575       if (changes.Files(fileslst) == false || fileslst.empty())
  576      return false;
  577       auto const basedir = flNotFile(File);
  578       for (auto && file: fileslst)
  579       {
  580      auto const name = flCombine(basedir, file.Path);
  581      AddVolatileFile(name, VolatileCmdL);
  582      if (file.Hashes.VerifyFile(name) == false)
  583         return _error->Error("The file %s does not match with the hashes in the %s file!", name.c_str(), File.c_str());
  584       }
  585       return true;
  586    }
  587    else
  588    {
  589       auto const filename = flNotDir(File);
  590       auto const Target = IndexTarget(File, filename, File, "file:" + File, false, true, {
  591      { "FILENAME", File },
  592      { "REPO_URI", "file:" + flAbsPath(flNotFile(File)) + '/' },
  593      { "COMPONENT", "volatile-packages-file" },
  594       });
  595       if (fileNameMatches(filename, "Packages"))
  596      AddVolatileFile(new debPackagesIndex(Target, true));
  597       else if (fileNameMatches(filename, "Sources"))
  598      AddVolatileFile(new debSourcesIndex(Target, true));
  599       else
  600      return false;
  601    }
  602 
  603    if (VolatileCmdL != nullptr)
  604       VolatileCmdL->push_back(File);
  605    return true;
  606 }
  607 bool pkgSourceList::AddVolatileFile(std::string const &File)
  608 {
  609    return AddVolatileFile(File, nullptr);
  610 }
  611                                     /*}}}*/
  612 void pkgSourceList::AddVolatileFiles(CommandLine &CmdL, std::vector<std::string> * const VolatileCmdL)/*{{{*/
  613 {
  614    std::remove_if(CmdL.FileList + 1, CmdL.FileList + 1 + CmdL.FileSize(), [&](char const * const I) {
  615       if (I != nullptr && (I[0] == '/' || (I[0] == '.' && (I[1] == '\0' || (I[1] == '.' && (I[2] == '\0' || I[2] == '/')) || I[1] == '/'))))
  616       {
  617      if (AddVolatileFile(I, VolatileCmdL))
  618         ;
  619      else
  620         _error->Error(_("Unsupported file %s given on commandline"), I);
  621      return true;
  622       }
  623       return false;
  624    });
  625 }
  626                                     /*}}}*/