Browse Source

bring back deb822 sources.list entries as .sources

Having two different formats in the same file is very dirty and causes
external tools to fail hard trying to parse them. It is probably not a
good idea for them to parse them in the first place, but they do and we
shouldn't break them if there is a better way.

So we solve this issue for now by giving our deb822 format a new
filename extension ".sources" which unsupporting applications are likely
to ignore an can begin gradually moving forward rather than waiting for
the unknown applications to catch up.

Currently and for the forseeable future apt is going to support both
with the same feature set as documented in the manpage, with the
longtime plan of adopting the 'new' format as default, but that is a
long way to go and might get going more from having an easier time
setting options than from us pushing it explicitely.
David Kalnischkies 8 years ago
parent
commit
81460e3296

+ 2 - 17
apt-pkg/policy.cc

@@ -396,21 +396,6 @@ APT_PURE signed short pkgPolicy::GetPriority(pkgCache::PkgFileIterator const &Fi
    return PFPriority[File->ID];
 }
 									/*}}}*/
-// PreferenceSection class - Overriding the default TrimRecord method	/*{{{*/
-// ---------------------------------------------------------------------
-/* The preference file is a user generated file so the parser should
-   therefore be a bit more friendly by allowing comments and new lines
-   all over the place rather than forcing a special format */
-class PreferenceSection : public pkgTagSection
-{
-   void TrimRecord(bool /*BeforeRecord*/, const char* &End)
-   {
-      for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r' || Stop[0] == '#'); Stop++)
-	 if (Stop[0] == '#')
-	    Stop = (const char*) memchr(Stop,'\n',End-Stop);
-   }
-};
-									/*}}}*/
 // ReadPinDir - Load the pin files from this dir into a Policy		/*{{{*/
 // ---------------------------------------------------------------------
 /* This will load each pin file in the given dir into a Policy. If the
@@ -455,8 +440,8 @@ bool ReadPinFile(pkgPolicy &Plcy,string File)
    pkgTagFile TF(&Fd);
    if (_error->PendingError() == true)
       return false;
-   
-   PreferenceSection Tags;
+
+   pkgUserTagSection Tags;
    while (TF.Step(Tags) == true)
    {
       // can happen when there are only comments in a record

+ 114 - 114
apt-pkg/sourcelist.cc

@@ -82,15 +82,15 @@ bool pkgSourceList::Type::FixupURI(string &URI) const
    return true;
 }
 									/*}}}*/
-bool pkgSourceList::Type::ParseStanza(vector<metaIndex *> &List,
+bool pkgSourceList::Type::ParseStanza(vector<metaIndex *> &List,	/*{{{*/
                                       pkgTagSection &Tags,
-                                      int i,
+                                      unsigned int const i,
                                       FileFd &Fd)
 {
    map<string, string> Options;
 
    string Enabled = Tags.FindS("Enabled");
-   if (Enabled.size() > 0 && StringToBool(Enabled) == false)
+   if (Enabled.empty() == false && StringToBool(Enabled) == false)
       return true;
 
    std::map<char const * const, char const * const> mapping;
@@ -115,46 +115,63 @@ bool pkgSourceList::Type::ParseStanza(vector<metaIndex *> &List,
    // now create one item per suite/section
    string Suite = Tags.FindS("Suites");
    Suite = SubstVar(Suite,"$(ARCH)",_config->Find("APT::Architecture"));
-   string const Section = Tags.FindS("Sections");
-   string URIS = Tags.FindS("URIs");
+   string const Component = Tags.FindS("Components");
+   string const URIS = Tags.FindS("URIs");
+
+   std::vector<std::string> const list_uris = VectorizeString(URIS, ' ');
+   std::vector<std::string> const list_suite = VectorizeString(Suite, ' ');
+   std::vector<std::string> const list_comp = VectorizeString(Component, ' ');
+
+   if (list_uris.empty())
+      // 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
+      return _error->Error(_("Malformed entry %u in %s file %s (%s)"), i, "sources", Fd.Name().c_str(), "URI");
 
-   std::vector<std::string> list_uris = StringSplit(URIS, " ");
-   std::vector<std::string> list_dist = StringSplit(Suite, " ");
-   std::vector<std::string> list_section = StringSplit(Section, " ");
-   
    for (std::vector<std::string>::const_iterator U = list_uris.begin();
         U != list_uris.end(); ++U)
    {
-      std::string URI = (*U);
-      if (!FixupURI(URI))
-      {
-         _error->Error(_("Malformed stanza %u in source list %s (URI parse)"),i,Fd.Name().c_str());
-         return false;
-      }
+      std::string URI = *U;
+      if (U->empty() || FixupURI(URI) == false)
+	 return _error->Error(_("Malformed entry %u in %s file %s (%s)"), i, "sources", Fd.Name().c_str(), "URI parse");
+
+      if (list_suite.empty())
+	 return _error->Error(_("Malformed entry %u in %s file %s (%s)"), i, "sources", Fd.Name().c_str(), "Suite");
 
-      for (std::vector<std::string>::const_iterator I = list_dist.begin();
-           I != list_dist.end(); ++I)
+      for (std::vector<std::string>::const_iterator S = list_suite.begin();
+           S != list_suite.end(); ++S)
       {
-         for (std::vector<std::string>::const_iterator J = list_section.begin();
-              J != list_section.end(); ++J)
-         {
-            if (CreateItem(List, URI, (*I), (*J), Options) == false)
-            {
-               return false;
-            }
-         }
+	 if (S->empty() == false && (*S)[S->size() - 1] == '/')
+	 {
+	    if (list_comp.empty() == false)
+	       return _error->Error(_("Malformed entry %u in %s file %s (%s)"), i, "sources", Fd.Name().c_str(), "absolute Suite Component");
+	    if (CreateItem(List, URI, *S, "", Options) == false)
+	       return false;
+	 }
+	 else
+	 {
+	    if (list_comp.empty())
+	       return _error->Error(_("Malformed entry %u in %s file %s (%s)"), i, "sources", Fd.Name().c_str(), "Component");
+
+	    for (std::vector<std::string>::const_iterator C = list_comp.begin();
+		  C != list_comp.end(); ++C)
+	    {
+	       if (CreateItem(List, URI, *S, *C, Options) == false)
+	       {
+		  return false;
+	       }
+	    }
+	 }
       }
    }
    return true;
 }
-
+									/*}}}*/
 // Type::ParseLine - Parse a single line				/*{{{*/
 // ---------------------------------------------------------------------
 /* This is a generic one that is the 'usual' format for sources.list
    Weird types may override this. */
 bool pkgSourceList::Type::ParseLine(vector<metaIndex *> &List,
 				    const char *Buffer,
-				    unsigned long const &CurLine,
+				    unsigned int const CurLine,
 				    string const &File) const
 {
    for (;Buffer != 0 && isspace(*Buffer); ++Buffer); // Skip whitespaces
@@ -171,10 +188,10 @@ bool pkgSourceList::Type::ParseLine(vector<metaIndex *> &List,
 	 // get one option, e.g. option1=value1
 	 string option;
 	 if (ParseQuoteWord(Buffer,option) == false)
-	    return _error->Error(_("Malformed line %lu in source list %s ([option] unparseable)"),CurLine,File.c_str());
+	    return _error->Error(_("Malformed entry %u in %s file %s (%s)"), CurLine, "list", File.c_str(), "[option] unparseable");
 
 	 if (option.length() < 3)
-	    return _error->Error(_("Malformed line %lu in source list %s ([option] too short)"),CurLine,File.c_str());
+	    return _error->Error(_("Malformed entry %u in %s file %s (%s)"), CurLine, "list", File.c_str(), "[option] too short");
 
 	 // accept options even if the last has no space before the ]-end marker
 	 if (option.at(option.length()-1) == ']')
@@ -185,16 +202,16 @@ bool pkgSourceList::Type::ParseLine(vector<metaIndex *> &List,
 
 	 size_t const needle = option.find('=');
 	 if (needle == string::npos)
-	    return _error->Error(_("Malformed line %lu in source list %s ([%s] is not an assignment)"),CurLine,File.c_str(), option.c_str());
+	    return _error->Error(_("Malformed entry %u in %s file %s (%s)"), CurLine, "list", File.c_str(), "[option] not assignment");
 
 	 string const key = string(option, 0, needle);
 	 string const value = string(option, needle + 1, option.length());
 
 	 if (key.empty() == true)
-	    return _error->Error(_("Malformed line %lu in source list %s ([%s] has no key)"),CurLine,File.c_str(), option.c_str());
+	    return _error->Error(_("Malformed entry %u in %s file %s (%s)"), CurLine, "list", File.c_str(), "[option] no key");
 
 	 if (value.empty() == true)
-	    return _error->Error(_("Malformed line %lu in source list %s ([%s] key %s has no value)"),CurLine,File.c_str(),option.c_str(),key.c_str());
+	    return _error->Error(_("Malformed entry %u in %s file %s (%s)"), CurLine, "list", File.c_str(), "[option] no value");
 
 	 Options[key] = value;
       }
@@ -207,33 +224,33 @@ bool pkgSourceList::Type::ParseLine(vector<metaIndex *> &List,
    string Section;
 
    if (ParseQuoteWord(Buffer,URI) == false)
-      return _error->Error(_("Malformed line %lu in source list %s (URI)"),CurLine,File.c_str());
+      return _error->Error(_("Malformed entry %u in %s file %s (%s)"), CurLine, "list", File.c_str(), "URI");
    if (ParseQuoteWord(Buffer,Dist) == false)
-      return _error->Error(_("Malformed line %lu in source list %s (dist)"),CurLine,File.c_str());
-      
+      return _error->Error(_("Malformed entry %u in %s file %s (%s)"), CurLine, "list", File.c_str(), "Suite");
+
    if (FixupURI(URI) == false)
-      return _error->Error(_("Malformed line %lu in source list %s (URI parse)"),CurLine,File.c_str());
+      return _error->Error(_("Malformed entry %u in %s file %s (%s)"), CurLine, "list", File.c_str(), "URI parse");
 
    // Check for an absolute dists specification.
    if (Dist.empty() == false && Dist[Dist.size() - 1] == '/')
    {
       if (ParseQuoteWord(Buffer,Section) == true)
-	 return _error->Error(_("Malformed line %lu in source list %s (absolute dist)"),CurLine,File.c_str());
+	 return _error->Error(_("Malformed entry %u in %s file %s (%s)"), CurLine, "list", File.c_str(), "absolute Suite Component");
       Dist = SubstVar(Dist,"$(ARCH)",_config->Find("APT::Architecture"));
       return CreateItem(List, URI, Dist, Section, Options);
    }
-   
+
    // Grab the rest of the dists
    if (ParseQuoteWord(Buffer,Section) == false)
-      return _error->Error(_("Malformed line %lu in source list %s (dist parse)"),CurLine,File.c_str());
-   
+      return _error->Error(_("Malformed entry %u in %s file %s (%s)"), CurLine, "list", File.c_str(), "Component");
+
    do
    {
       if (CreateItem(List, URI, Dist, Section, Options) == false)
 	 return false;
    }
    while (ParseQuoteWord(Buffer,Section) == true);
-   
+
    return true;
 }
 									/*}}}*/
@@ -242,11 +259,6 @@ bool pkgSourceList::Type::ParseLine(vector<metaIndex *> &List,
 /* */
 pkgSourceList::pkgSourceList() : d(NULL)
 {
-}
-
-pkgSourceList::pkgSourceList(string File) : d(NULL)
-{
-   Read(File);
 }
 									/*}}}*/
 // SourceList::~pkgSourceList - Destructor				/*{{{*/
@@ -305,7 +317,7 @@ void pkgSourceList::Reset()
 // SourceList::Read - Parse the sourcelist file				/*{{{*/
 // ---------------------------------------------------------------------
 /* */
-bool pkgSourceList::Read(string File)
+bool pkgSourceList::Read(string const &File)
 {
    Reset();
    return ReadAppend(File);
@@ -314,71 +326,63 @@ bool pkgSourceList::Read(string File)
 // SourceList::ReadAppend - Parse a sourcelist file			/*{{{*/
 // ---------------------------------------------------------------------
 /* */
-bool pkgSourceList::ReadAppend(string File)
+bool pkgSourceList::ReadAppend(string const &File)
 {
-   if (_config->FindB("APT::Sources::Use-Deb822", false) == true)
-   {
-      int lines_parsed =ParseFileDeb822(File);
-      if (lines_parsed < 0)
-         return false;
-      else if (lines_parsed > 0)
-         return true;
-      // no lines parsed  ... fall through and use old style parser
-   }
-   return ParseFileOldStyle(File);
+   if (flExtension(File) == "sources")
+      return ParseFileDeb822(File);
+   else
+      return ParseFileOldStyle(File);
 }
 
 // SourceList::ReadFileOldStyle - Read Traditional style sources.list 	/*{{{*/
 // ---------------------------------------------------------------------
 /* */
-bool pkgSourceList::ParseFileOldStyle(string File)
+bool pkgSourceList::ParseFileOldStyle(std::string const &File)
 {
    // Open the stream for reading
    ifstream F(File.c_str(),ios::in /*| ios::nocreate*/);
    if (F.fail() == true)
       return _error->Errno("ifstream::ifstream",_("Opening %s"),File.c_str());
 
-   // CNC:2003-12-10 - 300 is too short.
-   char Buffer[1024];
-
-   int CurLine = 0;
-   while (F.eof() == false)
+   std::string Buffer;
+   for (unsigned int CurLine = 1; std::getline(F, Buffer); ++CurLine)
    {
-      F.getline(Buffer,sizeof(Buffer));
-      CurLine++;
-      _strtabexpand(Buffer,sizeof(Buffer));
-      if (F.fail() && !F.eof())
-	 return _error->Error(_("Line %u too long in source list %s."),
-			      CurLine,File.c_str());
-
-      
-      char *I;
-      // CNC:2003-02-20 - Do not break if '#' is inside [].
-      for (I = Buffer; *I != 0 && *I != '#'; I++)
-         if (*I == '[')
-         {
-	    char *b_end = strchr(I + 1, ']');
-            if (b_end != NULL)
-               I = b_end;
-         }
-      *I = 0;
-      
-      const char *C = _strstrip(Buffer);
-      
-      // Comment or blank
-      if (C[0] == '#' || C[0] == 0)
+      // remove comments
+      size_t curpos = 0;
+      while ((curpos = Buffer.find('#', curpos)) != std::string::npos)
+      {
+	 size_t const openbrackets = std::count(Buffer.begin(), Buffer.begin() + curpos, '[');
+	 size_t const closedbrackets = std::count(Buffer.begin(), Buffer.begin() + curpos, ']');
+	 if (openbrackets > closedbrackets)
+	 {
+	    // a # in an option, unlikely, but oh well, it was supported so stick to it
+	    ++curpos;
+	    continue;
+	 }
+	 Buffer.erase(curpos);
+	 break;
+      }
+      // remove spaces before/after
+      curpos = Buffer.find_first_not_of(" \t\r");
+      if (curpos != 0)
+	 Buffer.erase(0, curpos);
+      curpos = Buffer.find_last_not_of(" \t\r");
+      if (curpos != std::string::npos)
+	 Buffer.erase(curpos + 1);
+
+      if (Buffer.empty())
 	 continue;
-      	    
+
       // Grok it
-      string LineType;
-      if (ParseQuoteWord(C,LineType) == false)
+      std::string const LineType = Buffer.substr(0, Buffer.find(' '));
+      if (LineType.empty() || LineType == Buffer)
 	 return _error->Error(_("Malformed line %u in source list %s (type)"),CurLine,File.c_str());
 
       Type *Parse = Type::GetType(LineType.c_str());
       if (Parse == 0)
 	 return _error->Error(_("Type '%s' is not known on line %u in source list %s"),LineType.c_str(),CurLine,File.c_str());
-      
-      if (Parse->ParseLine(SrcList, C, CurLine, File) == false)
+
+      if (Parse->ParseLine(SrcList, Buffer.c_str() + LineType.length(), CurLine, File) == false)
 	 return false;
    }
    return true;
@@ -387,30 +391,25 @@ bool pkgSourceList::ParseFileOldStyle(string File)
 // SourceList::ParseFileDeb822 - Parse deb822 style sources.list 	/*{{{*/
 // ---------------------------------------------------------------------
 /* Returns: the number of stanzas parsed*/
-int pkgSourceList::ParseFileDeb822(string File)
+bool pkgSourceList::ParseFileDeb822(string const &File)
 {
-   pkgTagSection Tags;
-   unsigned int i=0;
+   pkgUserTagSection Tags;
+   unsigned int i = 1;
 
    // see if we can read the file
-   _error->PushToStack();
    FileFd Fd(File, FileFd::ReadOnly);
    pkgTagFile Sources(&Fd);
    if (_error->PendingError() == true)
-   {
-      _error->RevertToStack();
-      return 0;
-   }
-   _error->MergeWithStack();
-   
+      return _error->Error(_("Malformed stanza %u in source list %s (type)"),i,File.c_str());
+
    // read step by step
    while (Sources.Step(Tags) == true)
    {
-      if(!Tags.Exists("Types")) 
-         continue;
+      if(Tags.Exists("Types") == false)
+	 return _error->Error(_("Malformed stanza %u in source list %s (type)"),i,File.c_str());
 
       string const types = Tags.FindS("Types");
-      std::vector<std::string> list_types = StringSplit(types, " ");
+      std::vector<std::string> const list_types = VectorizeString(types, ' ');
       for (std::vector<std::string>::const_iterator I = list_types.begin();
         I != list_types.end(); ++I)
       {
@@ -418,18 +417,16 @@ int pkgSourceList::ParseFileDeb822(string File)
          if (Parse == 0)
          {
             _error->Error(_("Type '%s' is not known on stanza %u in source list %s"), (*I).c_str(),i,Fd.Name().c_str());
-            return -1;
+            return false;
          }
-         
+
          if (!Parse->ParseStanza(SrcList, Tags, i, Fd))
-            return -1;
+            return false;
 
-         i++;
+         ++i;
       }
    }
-
-   // we are done, return the number of stanzas read
-   return i;
+   return true;
 }
 									/*}}}*/
 // SourceList::FindIndex - Get the index associated with a file		/*{{{*/
@@ -471,9 +468,12 @@ bool pkgSourceList::GetIndexes(pkgAcquire *Owner, bool GetAll) const
 // Based on ReadConfigDir()						/*{{{*/
 // ---------------------------------------------------------------------
 /* */
-bool pkgSourceList::ReadSourceDir(string Dir)
+bool pkgSourceList::ReadSourceDir(string const &Dir)
 {
-   vector<string> const List = GetListOfFilesInDir(Dir, "list", true);
+   std::vector<std::string> ext;
+   ext.push_back("list");
+   ext.push_back("sources");
+   std::vector<std::string> const List = GetListOfFilesInDir(Dir, ext, true);
 
    // Read the files
    for (vector<string>::const_iterator I = List.begin(); I != List.end(); ++I)

+ 8 - 8
apt-pkg/sourcelist.h

@@ -72,11 +72,11 @@ class pkgSourceList
       bool FixupURI(std::string &URI) const;
       virtual bool ParseStanza(std::vector<metaIndex *> &List,
                                pkgTagSection &Tags,
-                               int stanza_n,
+                               unsigned int const stanza_n,
                                FileFd &Fd);
       virtual bool ParseLine(std::vector<metaIndex *> &List,
 			     const char *Buffer,
-			     unsigned long const &CurLine,std::string const &File) const;
+			     unsigned int const CurLine,std::string const &File) const;
       virtual bool CreateItem(std::vector<metaIndex *> &List,std::string const &URI,
 			      std::string const &Dist,std::string const &Section,
 			      std::map<std::string, std::string> const &Options) const = 0;
@@ -90,18 +90,19 @@ class pkgSourceList
 
    std::vector<metaIndex *> SrcList;
 
-   int ParseFileDeb822(std::string File);
-   bool ParseFileOldStyle(std::string File);
+   private:
+   APT_HIDDEN bool ParseFileDeb822(std::string const &File);
+   APT_HIDDEN bool ParseFileOldStyle(std::string const &File);
 
    public:
 
    bool ReadMainList();
-   bool Read(std::string File);
+   bool Read(std::string const &File);
 
    // CNC:2003-03-03
    void Reset();
-   bool ReadAppend(std::string File);
-   bool ReadSourceDir(std::string Dir);
+   bool ReadAppend(std::string const &File);
+   bool ReadSourceDir(std::string const &Dir);
    
    // List accessors
    inline const_iterator begin() const {return SrcList.begin();};
@@ -117,7 +118,6 @@ class pkgSourceList
    time_t GetLastModifiedTime();
 
    pkgSourceList();
-   explicit pkgSourceList(std::string File);
    virtual ~pkgSourceList();
 };
 

+ 8 - 0
apt-pkg/tagfile.cc

@@ -775,6 +775,14 @@ bool pkgTagSection::Write(FileFd &File, char const * const * const Order, std::v
 }
 									/*}}}*/
 
+void pkgUserTagSection::TrimRecord(bool /*BeforeRecord*/, const char* &End)/*{{{*/
+{
+   for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r' || Stop[0] == '#'); Stop++)
+      if (Stop[0] == '#')
+	 Stop = (const char*) memchr(Stop,'\n',End-Stop);
+}
+									/*}}}*/
+
 #include "tagfile-order.c"
 
 // TFRewrite - Rewrite a control record					/*{{{*/

+ 8 - 0
apt-pkg/tagfile.h

@@ -142,6 +142,14 @@ class pkgTagSection
    bool Write(FileFd &File, char const * const * const Order = NULL, std::vector<Tag> const &Rewrite = std::vector<Tag>()) const;
 };
 
+
+/* For user generated file the parser should be a bit more relaxed in exchange
+   for being a bit slower to allow comments and new lines all over the place */
+class pkgUserTagSection : public pkgTagSection
+{
+   virtual void TrimRecord(bool BeforeRecord, const char* &End);
+};
+
 class pkgTagFilePrivate;
 class pkgTagFile
 {

+ 228 - 109
doc/sources.list.5.xml

@@ -31,37 +31,99 @@
  
  <refsect1><title>Description</title>
    <para>
-   The source list <filename>/etc/apt/sources.list</filename> is designed to support
-   any number of active sources and a variety of source media. The file lists one
-   source per line, with the most preferred source listed first. The information available
-   from the configured sources is acquired by <command>apt-get update</command>
-   (or by an equivalent command from another APT front-end).
-   </para>
-   <para>
-   Each line specifying a source starts with type (e.g. <literal>deb-src</literal>)
-   followed by options and arguments for this type.
-   Individual entries cannot be continued onto a following line. Empty lines
-   are ignored, and a <literal>#</literal> character anywhere on a line marks
-   the remainder of that line as a comment.
+      The source list <filename>/etc/apt/sources.list</filename> and the the
+      files contained in <filename>/etc/apt/sources.list.d/</filename> are
+      designed to support any number of active sources and a variety of source
+      media. The files list one source per line (one line style) or contain multiline
+      stanzas defining one or more sources per stanza (deb822 style), with the
+      most preferred source listed first. The information available from the
+      configured sources is acquired by <command>apt-get update</command> (or
+      by an equivalent command from another APT front-end).
    </para>
  </refsect1>
  
  <refsect1><title>sources.list.d</title>
-   <para>The <filename>/etc/apt/sources.list.d</filename> directory provides
-   a way to add sources.list entries in separate files.
-   The format is the same as for the regular <filename>sources.list</filename> file.
-   File names need to end with
-   <filename>.list</filename> and may only contain letters (a-z and A-Z),
-   digits (0-9), underscore (_), hyphen (-) and period (.) characters.
-   Otherwise APT will print a notice that it has ignored a file, unless that
-   file matches a pattern in the <literal>Dir::Ignore-Files-Silently</literal>
-   configuration list - in which case it will be silently ignored.</para>
+    <para>The <filename>/etc/apt/sources.list.d</filename> directory provides
+       a way to add sources.list entries in separate files.
+       Two different file formats are allowed as described in the next two sections.
+       Filenames need to have either the extension <filename>.list</filename> or
+       <filename>.sources</filename> depending on the contained format.
+       The filenames may only contain letters (a-z and A-Z),
+       digits (0-9), underscore (_), hyphen (-) and period (.) characters.
+       Otherwise APT will print a notice that it has ignored a file, unless that
+       file matches a pattern in the <literal>Dir::Ignore-Files-Silently</literal>
+       configuration list - in which case it will be silently ignored.</para>
  </refsect1>
 
- <refsect1><title>The deb and deb-src types</title>
+ <refsect1><title>one line style format</title>
+    <para>
+       Files in this format have the extension <filename>.list</filename>.
+       Each line specifying a source starts with a type (e.g. <literal>deb-src</literal>)
+       followed by options and arguments for this type.
+
+       Individual entries cannot be continued onto a following line. Empty lines
+       are ignored, and a <literal>#</literal> character anywhere on a line marks
+       the remainder of that line as a comment. Consequently an entry can be
+       disabled by commenting out the entire line.
+
+       If options should be provided they are separated by spaces and all of
+       them together are enclosed by square brackets (<literal>[]</literal>)
+       included in the line after the type separated from it with a space.
+       If an option allows multiple values these are separated from each other
+       with a comma (<literal>,</literal>). An option name is separated from its
+       value(s) by a equal sign (<literal>=</literal>). Multivalue options have
+       also <literal>-=</literal> and <literal>+=</literal> as separator which
+       instead of replacing the default with the given value(s) modify the default
+       value(s) to remove or include the given values.
+   </para><para>
+       This is the traditional format and supported by all apt versions.
+       Note that not all options as described below are supported by all apt versions.
+       Note also that some older applications parsing this format on its own might not
+       expect to encounter options as they were uncommon before the introduction of
+       multi-architecture support.
+    </para>
+ </refsect1>
+
+ <refsect1><title>deb822 style format</title>
+    <para>
+       Files in this format have the extension <filename>.sources</filename>.
+       The format is similar in syntax to other files used by Debian and its
+       derivatives, like the metadata itself apt will download from the configured
+       sources or the <filename>debian/control</filename> file in a Debian source package.
+
+       Individual entries are separated by an empty line, additional empty
+       lines are ignored, and a <literal>#</literal> character at the start of
+       the line marks the entire line as a comment. An entry can hence be
+       disabled by commenting out each line belonging to the stanza, but it is
+       usually easier to add the field "Enabled: no" to the stanza to disable
+       the entry. Removing the field or setting it to yes reenables it.
+
+       Options have the same syntax as every other field: A fieldname separated by
+       a colon (<literal>:</literal>) and optionally spaces from its value(s).
+       Note especially that multiple values are separated by spaces, not by
+       commas as in the one line format. Multivalue fields like <literal>Architectures</literal>
+       also have <literal>Architectures-Add</literal> and <literal>Architectures-Remove</literal>
+       to modify the default value rather than replacing it.
+   </para><para>
+       This is a new format supported by apt itself since version 1.1. Previous
+       versions ignore such files with a notice message as described earlier.
+       It is intended to make this format gradually the default format and
+       deprecating the previously described one line style format as it is
+       easier to create, extend and modify by humans and machines alike
+       especially if a lot of sources and/or options are involved.
+
+       Developers who are working with and/or parsing apt sources are highly
+       encouraged to add support for this format and to contact the APT team
+       to coordinate and share this work. Users can freely adopt this format
+       already, but could encounter problems with software not supporting
+       the format yet.
+    </para>
+ </refsect1>
+
+ <refsect1><title>The deb and deb-src types: General Format</title>
    <para>The <literal>deb</literal> type references a typical two-level Debian
    archive, <filename>distribution/component</filename>. The
-   <literal>distribution</literal> is generally an archive name like
+   <literal>distribution</literal> is generally a suite name like
    <literal>stable</literal> or <literal>testing</literal> or a codename like
    <literal>&stable-codename;</literal> or <literal>&testing-codename;</literal>
    while component is one of <literal>main</literal>, <literal>contrib</literal> or
@@ -70,42 +132,33 @@
    code in the same form as the <literal>deb</literal> type.
    A <literal>deb-src</literal> line is required to fetch source indexes.</para>
 
-   <para>The format for a <filename>sources.list</filename> entry using the
+   <para>The format for two one line style entries using the
    <literal>deb</literal> and <literal>deb-src</literal> types is:</para>
 
-   <literallayout>deb [ options ] uri suite [component1] [component2] [...]</literallayout>
+   <literallayout>deb [ option1=value1 option2=value2 ] uri suite [component1] [component2] [...]
+deb-src [ option1=value1 option2=value2 ] uri suite [component1] [component2] [...]</literallayout>
 
-   <para>Alternatively a rfc822 style format is also supported:
+   <para>Alternatively the equivalent entry in deb822 style looks like this:
    <literallayout>
      Types: deb deb-src
-     URIs: http://example.com
-     Suites: stable testing
-     Sections: component1 component2
-     Description: short
-      long long long
-     [option1]: [option1-value]
-
-     Types: deb
-     URIs: http://another.example.com
-     Suites: experimental
-     Sections: component1 component2
-     Enabled: no
-     Description: short
-      long long long
-     [option1]: [option1-value]
+     URIs: uri
+     Suites: suite
+     Components: [component1] [component2] [...]
+     option1: value1
+     option2: value2
    </literallayout>
    </para>
 
    <para>The URI for the <literal>deb</literal> type must specify the base of the
-   Debian distribution, from which APT will find the information it needs. 
-   <literal>suite</literal> can specify an exact path, in which case the 
+   Debian distribution, from which APT will find the information it needs.
+   <literal>suite</literal> can specify an exact path, in which case the
    components must be omitted and <literal>suite</literal> must end with
    a slash (<literal>/</literal>). This is useful for the case when only a
-   particular sub-section of the archive denoted by the URI is of interest.
+   particular sub-directory of the archive denoted by the URI is of interest.
    If <literal>suite</literal> does not specify an exact path, at least
    one <literal>component</literal> must be present.</para>
 
-   <para><literal>suite</literal> may also contain a variable, 
+   <para><literal>suite</literal> may also contain a variable,
    <literal>$(ARCH)</literal>
    which expands to the Debian architecture (such as <literal>amd64</literal> or
    <literal>armel</literal>) used on the system. This permits architecture-independent
@@ -113,67 +166,80 @@
    of interest when specifying an exact path, <literal>APT</literal> will
    automatically generate a URI with the current architecture otherwise.</para>
 
-   <para>In the traditional style sources.list format since only one
-   distribution can be specified per line it may be necessary to have
-   multiple lines for the same URI, if a subset of all available
-   distributions or components at that location is desired.  APT will
-   sort the URI list after it has generated a complete set internally,
-   and will collapse multiple references to the same Internet host,
-   for instance, into a single connection, so that it does not
-   inefficiently establish an FTP connection, close it, do something
-   else, and then re-establish a connection to that same host. This
-   feature is useful for accessing busy FTP sites with limits on the
-   number of simultaneous anonymous users. APT also parallelizes
-   connections to different hosts to more effectively deal with sites
-   with low bandwidth.</para>
-
-   <para><literal>options</literal> is always optional and needs to be surrounded by
-   square brackets. It can consist of multiple settings in the form
-   <literal><replaceable>setting</replaceable>=<replaceable>value</replaceable></literal>.
-   Multiple settings are separated by spaces. The following settings are supported by APT
-   (note however that unsupported settings will be ignored silently):
-   <itemizedlist>
-   <listitem><para><literal>arch=<replaceable>arch1</replaceable>,<replaceable>arch2</replaceable>,…</literal>
-   can be used to specify for which architectures information should
-   be downloaded. If this option is not set all architectures defined by the
-   <literal>APT::Architectures</literal> option will be downloaded.</para></listitem>
-
-   <listitem><para><literal>arch+=<replaceable>arch1</replaceable>,<replaceable>arch2</replaceable>,…</literal>
-   and <literal>arch-=<replaceable>arch1</replaceable>,<replaceable>arch2</replaceable>,…</literal>
-   which can be used to add/remove architectures from the set which will be downloaded.</para></listitem>
-
-   <listitem><para><literal>lang=<replaceable>lang1</replaceable>,<replaceable>lang2</replaceable>,…</literal>,
-   <literal>lang+=<replaceable>lang1</replaceable>,<replaceable>lang2</replaceable>,…</literal> and
-   <literal>lang-=<replaceable>lang1</replaceable>,<replaceable>lang2</replaceable>,…</literal> functioning in
-   the same way as the <literal>arch</literal>-options described before. They can be used to specify for
-   which languages apt will acquire metadata, like translated package descriptions, for. If not specified, the
-   default set is defined by the <literal>Acquire::Languages</literal> config option.</para></listitem>
-
-   <listitem><para><literal>target=<replaceable>target1</replaceable>,<replaceable>target2</replaceable>,…</literal>,
-   <literal>target+=<replaceable>target1</replaceable>,<replaceable>target2</replaceable>,…</literal> and
-   <literal>target-=<replaceable>target1</replaceable>,<replaceable>target2</replaceable>,…</literal> again functioning in
-   the same way as the <literal>arch</literal>-options described before. They can be used to specify which
-   targets apt will try to acquire from this source. If not specified, the default set is defined by
-   the <literal>APT::Acquire::Targets</literal> configuration scope.</para></listitem>
-
-   <listitem><para><literal>trusted=yes</literal> can be set to indicate that packages
-   from this source are always authenticated even if the <filename>Release</filename> file
-   is not signed or the signature can't be checked. This disables parts of &apt-secure;
-   and should therefore only be used in a local and trusted context. <literal>trusted=no</literal>
-   is the opposite which handles even correctly authenticated sources as not authenticated.</para></listitem>
-   </itemizedlist></para>
+   <para>Especially in the one line style format since only one distribution
+      can be specified per line it may be necessary to have multiple lines for
+      the same URI, if a subset of all available distributions or components at
+      that location is desired.  APT will sort the URI list after it has
+      generated a complete set internally, and will collapse multiple
+      references to the same Internet host, for instance, into a single
+      connection, so that it does not inefficiently establish a
+      connection, close it, do something else, and then re-establish a
+      connection to that same host. APT also parallelizes connections to
+      different hosts to more effectively deal with sites with low
+      bandwidth.</para>
 
    <para>It is important to list sources in order of preference, with the most
    preferred source listed first. Typically this will result in sorting
    by speed from fastest to slowest (CD-ROM followed by hosts on a local
    network, followed by distant Internet hosts, for example).</para>
 
-   <para>Some examples:</para>
-   <literallayout>
-deb http://ftp.debian.org/debian &stable-codename; main contrib non-free
-deb http://security.debian.org/ &stable-codename;/updates main contrib non-free
-   </literallayout>
+   <para>As an example, the sources for your distribution could look like this
+      in one line style format:
+      <literallayout>&sourceslist-list-format;</literallayout> or like this in
+      deb822 style format:
+      <literallayout>&sourceslist-sources-format;</literallayout></para>
+ </refsect1>
 
+ <refsect1><title>The deb and deb-src types: Options</title>
+    <para>Each source entry can have options specified modifying which and how
+       the source is accessed and data acquired from it. Format, syntax and names
+       of the options varies between the two formats one line and deb822 style
+       as described, but they have both the same options available. For simplicity
+       we list the deb822 fieldname and provide the one line name in brackets.
+       Remember that beside setting multivalue options explicitly, there is also
+       the option to modify them based on the default, but we aren't listing those
+       names explicitly here. Unsupported options are silently ignored by all
+       APT versions.
+
+       <itemizedlist>
+	  <listitem><para><literal>Architectures</literal>
+		(<literal>arch</literal>) is a multivalue option defining for
+		which architectures information should be downloaded. If this
+		option isn't set the default is all architectures as defined by
+		the <literal>APT::Architectures</literal> config option.
+	  </para></listitem>
+
+	  <listitem><para><literal>Languages</literal>
+		(<literal>lang</literal>) is a multivalue option defining for
+		which languages information like translated package
+		descriptions should be downloaded.  If this option isn't set
+		the default is all languages as defined by the
+		<literal>Acquire::Languages</literal> config option.
+	  </para></listitem>
+
+	  <listitem><para><literal>Targets</literal>
+		(<literal>target</literal>) is a multivalue option defining
+		which download targets apt will try to acquire from this
+		source. If not specified, the default set is defined by the
+		<literal>APT::Acquire::Targets</literal> configuration scope.
+	  </para></listitem>
+
+	  <listitem><para><literal>Trusted</literal> (<literal>trusted</literal>)
+		is a tri-state value which defaults to APT deciding if a source
+		is considered trusted or if warnings should be raised before e.g.
+		packages are installed from this source. This option can be used
+		to override this decision either with the value <literal>yes</literal>,
+		which lets APT consider this source always as a trusted source
+		even if it has no or fails authentication checks by disabling parts
+		of &apt-secure; and should therefore only be used in a local and trusted
+		context (if at all) as otherwise security is breached. The opposite
+		can be achieved with the value no, which causes the source to be handled
+		as untrusted even if the authentication checks passed successfully.
+		The default value can't be set explicitly.
+	  </para></listitem>
+       </itemizedlist>
+
+    </para>
  </refsect1>
 
  <refsect1><title>URI specification</title>
@@ -247,34 +313,70 @@ deb http://security.debian.org/ &stable-codename;/updates main contrib non-free
  </refsect1>
  
  <refsect1><title>Examples</title>
-   <para>Uses the archive stored locally (or NFS mounted) at /home/jason/debian
+   <para>Uses the archive stored locally (or NFS mounted) at /home/apt/debian
    for stable/main, stable/contrib, and stable/non-free.</para>
-   <literallayout>deb file:/home/jason/debian stable main contrib non-free</literallayout>
+   <literallayout>deb file:/home/apt/debian stable main contrib non-free</literallayout>
+   <literallayout>Types: deb
+URIs: file:/home/apt/debian
+Suites: stable
+Components: main contrib non-free</literallayout>
 
    <para>As above, except this uses the unstable (development) distribution.</para>
-   <literallayout>deb file:/home/jason/debian unstable main contrib non-free</literallayout>
+   <literallayout>deb file:/home/apt/debian unstable main contrib non-free</literallayout>
+   <literallayout>Types: deb
+URIs: file:/home/apt/debian
+Suites: unstable
+Components: main contrib non-free</literallayout>
 
    <para>Source line for the above</para>
-   <literallayout>deb-src file:/home/jason/debian unstable main contrib non-free</literallayout>
+   <literallayout>deb-src file:/home/apt/debian unstable main contrib non-free</literallayout>
+   <literallayout>Types: deb-src
+URIs: file:/home/apt/debian
+Suites: unstable
+Components: main contrib non-free</literallayout>
+
 
    <para>The first line gets package information for the architectures in <literal>APT::Architectures</literal>
    while the second always retrieves <literal>amd64</literal> and <literal>armel</literal>.</para>
-   <literallayout>deb http://ftp.debian.org/debian &stable-codename; main
-deb [ arch=amd64,armel ] http://ftp.debian.org/debian &stable-codename; main</literallayout>
+   <literallayout>deb http://httpredir.debian.org/debian &stable-codename; main
+deb [ arch=amd64,armel ] http://httpredir.debian.org/debian &stable-codename; main</literallayout>
+   <literallayout>Types: deb
+URIs: http://httpredir.debian.org/debian
+Suites: &stable-codename;
+Components: main
+
+Types: deb
+URIs: http://httpredir.debian.org/debian
+Suites: &stable-codename;
+Components: main
+Architectures: amd64 armel
+</literallayout>
 
    <para>Uses HTTP to access the archive at archive.debian.org, and uses only
    the hamm/main area.</para>
    <literallayout>deb http://archive.debian.org/debian-archive hamm main</literallayout>
+   <literallayout>Types: deb
+URIs: http://archive.debian.org/debian-archive
+Suites: hamm
+Components: main</literallayout>
 
    <para>Uses FTP to access the archive at ftp.debian.org, under the debian
    directory, and uses only the &stable-codename;/contrib area.</para>
    <literallayout>deb ftp://ftp.debian.org/debian &stable-codename; contrib</literallayout>
+   <literallayout>Types: deb
+URIs: ftp://ftp.debian.org/debian
+Suites: &stable-codename;
+Components: contrib</literallayout>
 
    <para>Uses FTP to access the archive at ftp.debian.org, under the debian
    directory, and uses only the unstable/contrib area. If this line appears as
    well as the one in the previous example in <filename>sources.list</filename>
    a single FTP session will be used for both resource lines.</para>
    <literallayout>deb ftp://ftp.debian.org/debian unstable contrib</literallayout>
+   <literallayout>Types: deb
+URIs: ftp://ftp.debian.org/debian
+Suites: unstable
+Components: contrib</literallayout>
 
    <para>Uses HTTP to access the archive at ftp.tlh.debian.org, under the
    universe directory, and uses only files found under
@@ -284,15 +386,32 @@ deb [ arch=amd64,armel ] http://ftp.debian.org/debian &stable-codename; main</li
    illustrates how to use the substitution variable; official debian
    archives are not structured like this]
    <literallayout>deb http://ftp.tlh.debian.org/universe unstable/binary-$(ARCH)/</literallayout>
+   <literallayout>Types: deb
+URIs: http://ftp.tlh.debian.org/universe
+Suites: unstable/binary-$(ARCH)/</literallayout>
    </para>
+
+   <para>Uses HTTP to get binary packages as well as sources from the stable, testing and unstable
+      suites and the components main and contrib.</para>
+   <literallayout>deb http://httpredir.debian.org/debian stable main contrib
+deb-src http://httpredir.debian.org/debian stable main contrib
+deb http://httpredir.debian.org/debian testing main contrib
+deb-src http://httpredir.debian.org/debian testing main contrib
+deb http://httpredir.debian.org/debian unstable main contrib
+deb-src http://httpredir.debian.org/debian unstable main contrib</literallayout>
+   <literallayout>Types: deb deb-src
+URIs: http://httpredir.debian.org/debian
+Suites: stable testing unstable
+Components: main contrib
+</literallayout>
+
  </refsect1>
- 
+
  <refsect1><title>See Also</title>
-   <para>&apt-cache; &apt-conf;
+   <para>&apt-get;, &apt-conf;
    </para>
  </refsect1>
 
  &manbugs;
- 
-</refentry>
 
+</refentry>

+ 56 - 19
test/integration/test-apt-sources-deb822

@@ -7,9 +7,8 @@ TESTDIR=$(readlink -f $(dirname $0))
 setupenvironment
 configarchitecture 'i386'
 
-echo 'APT::Sources::Use-Deb822 "true";' > rootdir/etc/apt/apt.conf.d/00enabledeb822
-
-SOURCES='rootdir/etc/apt/sources.list'
+LISTS='rootdir/etc/apt/sources.list.d/test.list'
+SOURCES='rootdir/etc/apt/sources.list.d/test.sources'
 BASE='# some comment
 # that contains a : as well
 #Types: meep
@@ -17,23 +16,48 @@ BASE='# some comment
 Types: deb
 URIs: http://ftp.debian.org/debian
 Suites: stable
-Sections: main
+Components: main
 Description: summay
  and the long part'
 
-msgtest 'Test sources.list' 'old style'
-echo "deb http://ftp.debian.org/debian stable main" > $SOURCES
+msgcleantest() {
+	 rm -f $LISTS $SOURCES
+	 msgtest "$@"
+}
+
+msgcleantest 'Test sources.list' 'old style'
+echo "deb http://ftp.debian.org/debian stable main" > $LISTS
+testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 
+'http://ftp.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-i386_Packages 0 
+'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 " aptget update --print-uris
+
+msgcleantest 'Test sources.list' 'old style with options'
+echo "deb [trusted=yes arch+=armel,powerpc] http://ftp.debian.org/debian stable main" > $LISTS
+testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 
+'http://ftp.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-i386_Packages 0 
+'http://ftp.debian.org/debian/dists/stable/main/binary-armel/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-armel_Packages 0 
+'http://ftp.debian.org/debian/dists/stable/main/binary-powerpc/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-powerpc_Packages 0 
+'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 " aptget update --print-uris
+
+msgcleantest 'Test sources.list' 'old style with comments'
+echo "deb http://ftp.debian.org/debian stable main # non-free" > $LISTS
+testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 
+'http://ftp.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-i386_Packages 0 
+'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 " aptget update --print-uris
+
+msgcleantest 'Test sources.list' 'old style with option comments'
+echo "deb [trusted=yes#yeahreally] http://ftp.debian.org/debian stable main # non-free" > $LISTS
 testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 
 'http://ftp.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-i386_Packages 0 
 'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 " aptget update --print-uris
 
-msgtest 'Test sources.list' 'simple deb822'
+msgcleantest 'Test sources.list' 'simple deb822'
 echo "$BASE"  > $SOURCES
 testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 
 'http://ftp.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-i386_Packages 0 
 'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 " aptget update --print-uris
 
-msgtest 'Test deb822 with' 'two entries'
+msgcleantest 'Test deb822 with' 'two entries'
 # Two entries
 echo "$BASE" > $SOURCES
 echo "" >> $SOURCES
@@ -46,7 +70,7 @@ testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.deb
 'http://ftp.debian.org/debian/dists/unstable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_unstable_main_i18n_Translation-en 0 " aptget update --print-uris
 
 # two suite entries
-msgtest 'Test deb822 with' 'two Suite entries'
+msgcleantest 'Test deb822 with' 'two Suite entries'
 echo "$BASE"  | sed -e "s/stable/stable unstable/" > $SOURCES
 testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 
 'http://ftp.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-i386_Packages 0 
@@ -55,7 +79,7 @@ testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.deb
 'http://ftp.debian.org/debian/dists/unstable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_unstable_main_binary-i386_Packages 0 
 'http://ftp.debian.org/debian/dists/unstable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_unstable_main_i18n_Translation-en 0 " aptget update --print-uris
 
-msgtest 'Test deb822' 'architecture option'
+msgcleantest 'Test deb822' 'architecture option'
 echo "$BASE" > $SOURCES
 echo "Architectures: amd64 armel" >> $SOURCES
 testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 
@@ -63,17 +87,30 @@ testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.deb
 'http://ftp.debian.org/debian/dists/stable/main/binary-armel/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-armel_Packages 0 
 'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 " aptget update --print-uris
 
-
-msgtest 'Test old-style sources.list file which has' 'malformed dist'
-echo "deb http://ftp.debian.org" > $SOURCES
-testequal --nomsg "E: Malformed line 1 in source list $TMPWORKINGDIRECTORY/rootdir/etc/apt/sources.list (dist)
+msgcleantest 'Test old-style' 'suite arch variable'
+echo 'deb http://ftp.tlh.debian.org/universe unstable/binary-$(ARCH)/' > $LISTS
+testequal --nomsg "'http://ftp.tlh.debian.org/universe/unstable/binary-i386/InRelease' ftp.tlh.debian.org_universe_unstable_binary-i386_InRelease 0 
+'http://ftp.tlh.debian.org/universe/unstable/binary-i386/Packages.bz2' ftp.tlh.debian.org_universe_unstable_binary-i386_Packages 0 
+'http://ftp.tlh.debian.org/universe/unstable/binary-i386/en.bz2' ftp.tlh.debian.org_universe_unstable_binary-i386_en 0 " aptget update --print-uris
+
+msgcleantest 'Test deb822' 'suite arch variable'
+echo 'Types: deb
+URIs: http://ftp.tlh.debian.org/universe
+Suites: stable/binary-$(ARCH)/' > $SOURCES
+testequal --nomsg "'http://ftp.tlh.debian.org/universe/stable/binary-i386/InRelease' ftp.tlh.debian.org_universe_stable_binary-i386_InRelease 0 
+'http://ftp.tlh.debian.org/universe/stable/binary-i386/Packages.bz2' ftp.tlh.debian.org_universe_stable_binary-i386_Packages 0 
+'http://ftp.tlh.debian.org/universe/stable/binary-i386/en.bz2' ftp.tlh.debian.org_universe_stable_binary-i386_en 0 " aptget update --print-uris
+
+msgcleantest 'Test old-style sources.list file which has' 'malformed dist'
+echo "deb http://ftp.debian.org" > $LISTS
+testequal --nomsg "E: Malformed entry 1 in list file $TMPWORKINGDIRECTORY/$LISTS (Suite)
 E: The list of sources could not be read."  aptget update --print-uris
 
-msgtest 'Test deb822 sources.list file which has' 'malformed URI'
+msgcleantest 'Test deb822 sources.list file which has' 'malformed URI'
 echo "Types: deb
 Suites: stable
 " > $SOURCES
-testequal --nomsg  "E: Malformed stanza 0 in source list $TMPWORKINGDIRECTORY/rootdir/etc/apt/sources.list (URI parse)
+testequal --nomsg  "E: Malformed entry 1 in sources file $TMPWORKINGDIRECTORY/$SOURCES (URI)
 E: The list of sources could not be read."  aptget update --print-uris
 
 # with Enabled: false
@@ -82,7 +119,7 @@ echo "Enabled: no" >> $SOURCES
 testempty aptget update --print-uris
 
 # multiple URIs
-msgtest 'Test deb822 sources.list file which has' 'Multiple URIs work'
+msgcleantest 'Test deb822 sources.list file which has' 'Multiple URIs work'
 echo "$BASE"  | sed -e 's#http://ftp.debian.org/debian#http://ftp.debian.org/debian http://ftp.de.debian.org/debian#' > $SOURCES
 testequal --nomsg  "'http://ftp.de.debian.org/debian/dists/stable/InRelease' ftp.de.debian.org_debian_dists_stable_InRelease 0 
 'http://ftp.de.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.de.debian.org_debian_dists_stable_main_binary-i386_Packages 0 
@@ -92,7 +129,7 @@ testequal --nomsg  "'http://ftp.de.debian.org/debian/dists/stable/InRelease' ftp
 'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 " aptget update --print-uris
 
 # multiple Type in one field
-msgtest 'Test deb822 sources.list file which has' 'Multiple Types work'
+msgcleantest 'Test deb822 sources.list file which has' 'Multiple Types work'
 echo "$BASE"  | sed -e 's#Types: deb#Types: deb deb-src#' > $SOURCES
 testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 
 'http://ftp.debian.org/debian/dists/stable/main/source/Sources.bz2' ftp.debian.org_debian_dists_stable_main_source_Sources 0 
@@ -100,7 +137,7 @@ testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.deb
 'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 " aptget update --print-uris
 
 # a Suite
-msgtest 'Test deb822 sources.list file which has' 'a exact path and no sections'
+msgcleantest 'Test deb822 sources.list file which has' 'an exact path and no sections'
 cat > $SOURCES <<EOF
 Types: deb
 URIs: http://emacs.naquadah.org

+ 9 - 2
test/libapt/file-helpers.cc

@@ -56,10 +56,17 @@ void helperCreateLink(std::string const &dir, std::string const &targetname, std
 void helperCreateTemporaryFile(std::string const &id, FileFd &fd, std::string * const filename, char const * const content)
 {
    std::string name("apt-test-");
-   name.append(id).append(".XXXXXXXX");
+   name.append(id);
+   size_t const giventmp = name.find(".XXXXXX.");
+   if (giventmp == std::string::npos)
+      name.append(".XXXXXX");
    char * tempfile = strdup(name.c_str());
    ASSERT_STRNE(NULL, tempfile);
-   int tempfile_fd = mkstemp(tempfile);
+   int tempfile_fd;
+   if (giventmp == std::string::npos)
+      tempfile_fd = mkstemp(tempfile);
+   else
+      tempfile_fd = mkstemps(tempfile, name.length() - (giventmp + 7));
    ASSERT_NE(-1, tempfile_fd);
    if (filename != NULL)
       *filename = tempfile;

+ 5 - 10
test/libapt/sourcelist_test.cc

@@ -12,31 +12,26 @@
 
 #include "file-helpers.h"
 
-class SourceList : public pkgSourceList {
-   public:
-      using pkgSourceList::ParseFileDeb822;
-};
-
 TEST(SourceListTest,ParseFileDeb822)
 {
    FileFd fd;
    std::string tempfile;
-   createTemporaryFile("parsefiledeb822", fd, &tempfile,
+   createTemporaryFile("parsefiledeb822.XXXXXX.sources", fd, &tempfile,
       "Types: deb\n"
       "URIs: http://ftp.debian.org/debian\n"
       "Suites: stable\n"
-      "Sections: main\n"
+      "Components: main\n"
       "Description: short\n"
       " long description that can be very long\n"
       "\n"
       "Types: deb\n"
       "URIs: http://ftp.debian.org/debian\n"
       "Suites: unstable\n"
-      "Sections: main non-free\n");
+      "Components: main non-free\n");
    fd.Close();
 
-   SourceList sources;
-   EXPECT_EQ(2, sources.ParseFileDeb822(tempfile));
+   pkgSourceList sources;
+   EXPECT_EQ(true, sources.Read(tempfile));
    EXPECT_EQ(2, sources.size());
 
    if (tempfile.empty() == false)

+ 7 - 1
vendor/README

@@ -44,6 +44,10 @@ apt-key script and the keyring-package in particular as a dependency for apt.
 
 The field current-codename is optional and can be used in sources.list.in.
 
+The fields sourceslist-list-format and sourceslist-sources-format are used as
+examples in the sources.list manpage and the first one is additionally
+available in the sources.list.in template.
+They should in general reflect the default sources of your distro.
 
 == sources.list.in
 
@@ -55,6 +59,7 @@ You can use some placeholders in this file, namely:
 * &debian-oldstable-codename;
 * &debian-testing-codename;
 * &ubuntu-codename;
+* &sourceslist-list-format;
 with the value you would expect based on the name.
 
 The placeholder &current-codename; is yours and can be set in apt-vendor.ent
@@ -63,4 +68,5 @@ The placeholder &current-codename; is yours and can be set in apt-vendor.ent
 == apt.conf-*
 
 Files in your vendor directory following this naming scheme will be picked up
-by the debian/rules file and installed in /etc/apt/apt.conf.d/ directory.
+by the debian/rules file and installed in /etc/apt/apt.conf.d/ directory, with
+"apt.conf-" removed from the beginning of the filename.

+ 9 - 0
vendor/blankon/apt-vendor.ent

@@ -6,3 +6,12 @@
 <!ENTITY keyring-master-filename "/usr/share/keyrings/blankon-master-keyring.gpg">
 <!ENTITY keyring-uri "http://arsip.blankonlinux.or.id/blankon/project/blankon-archive-keyring.gpg">
 <!ENTITY current-codename "tambora">
+
+<!ENTITY sourceslist-list-format "deb http://arsip.blankonlinux.or.id/blankon &current-codename; main restricted
+deb http://arsip.blankonlinux.or.id/blankon &current-codename;-security main restricted
+deb http://arsip.blankonlinux.or.id/blankon &current-codename;-updates main restricted">
+
+<!ENTITY sourceslist-sources-format "Types: deb
+URIs: http://arsip.blankonlinux.or.id/blankon
+Suites: &current-codename; &current-codename;-security &current-codename;-updates
+Components: main restricted">

+ 12 - 0
vendor/debian/apt-vendor.ent

@@ -5,3 +5,15 @@
 <!ENTITY keyring-removed-filename "<filename>/usr/share/keyrings/debian-archive-removed-keys.gpg</filename>">
 <!ENTITY keyring-master-filename "">
 <!ENTITY keyring-uri "">
+
+<!ENTITY sourceslist-list-format "deb http://httpredir.debian.org/debian &stable-codename; main contrib non-free
+deb http://security.debian.org &stable-codename;/updates main contrib non-free">
+<!ENTITY sourceslist-sources-format "Types: deb
+URIs: http://httpredir.debian.org/debian
+Suites: &stable-codename;
+Components: main contrib non-free
+
+Types: deb
+URIs: http://security.debian.org
+Suites: &stable-codename;/updates
+Components: main contrib non-free">

+ 1 - 2
vendor/debian/sources.list.in

@@ -1,7 +1,6 @@
 # See sources.list(5) manpage for more information
 # Remember that CD-ROMs, DVDs and such are managed through the apt-cdrom tool.
-deb http://ftp.us.debian.org/debian &debian-stable-codename; main contrib non-free
-deb http://security.debian.org &debian-stable-codename;/updates main contrib non-free
+&sourceslist-list-format;
 
 # Uncomment if you want the apt-get source function to work
 #deb-src http://ftp.us.debian.org/debian &debian-stable-codename; main contrib non-free

+ 6 - 0
vendor/raspbian/apt-vendor.ent

@@ -5,3 +5,9 @@
 <!ENTITY keyring-removed-filename "<filename>/usr/share/keyrings/raspbian-archive-removed-keys.gpg</filename>">
 <!ENTITY keyring-master-filename "">
 <!ENTITY keyring-uri "">
+
+<!ENTITY sourceslist-list-format "deb http://mirrordirector.raspbian.org/raspbian &stable-codename; main contrib non-free">
+<!ENTITY sourceslist-sources-format "Types: deb
+URIs: http://mirrordirector.raspbian.org/raspbian
+Suites: &stable-codename;
+Components: main contrib non-free">

+ 7 - 0
vendor/steamos/apt-vendor.ent

@@ -6,3 +6,10 @@
 <!ENTITY keyring-master-filename "">
 <!ENTITY keyring-uri "">
 <!ENTITY current-codename "alchemist">
+
+<!ENTITY sourceslist-list-format "deb http://repo.steampowered.com/steamos &current-codename; main contrib non-free
+deb-src http://repo.steampowered.com/steamos &current-codename; main contrib non-free">
+<!ENTITY sourceslist-sources-format "Types: deb deb-src
+URIs: http://repo.steampowered.com/steamos
+Suites: &current-codename;
+Components: main contrib non-free">

+ 1 - 3
vendor/steamos/sources.list.in

@@ -1,5 +1,3 @@
 # See sources.list(5) manpage for more information
 # Remember that CD-ROMs, DVDs and such are managed through the apt-cdrom tool.
-
-deb http://repo.steampowered.com/steamos &current-codename; main contrib non-free
-deb-src http://repo.steampowered.com/steamos &current-codename; main contrib non-free
+&sourceslist-list-format;

+ 6 - 0
vendor/tanglu/apt-vendor.ent

@@ -6,3 +6,9 @@
 <!ENTITY keyring-master-filename "">
 <!ENTITY keyring-uri "">
 <!ENTITY current-codename "bartholomea">
+
+<!ENTITY sourceslist-list-format "deb http://archive.tanglu.org/tanglu &current-codename; main contrib non-free">
+<!ENTITY sourceslist-sources-format "Types: deb
+URIs: http://archive.tanglu.org/tanglu
+Suites: &current-codename;
+Components: main contrib non-free">

+ 13 - 0
vendor/ubuntu/apt-vendor.ent

@@ -5,3 +5,16 @@
 <!ENTITY keyring-removed-filename "<filename>/usr/share/keyrings/ubuntu-archive-removed-keys.gpg</filename>">
 <!ENTITY keyring-master-filename "/usr/share/keyrings/ubuntu-master-keyring.gpg">
 <!ENTITY keyring-uri "http://archive.ubuntu.com/ubuntu/project/ubuntu-archive-keyring.gpg">
+
+<!ENTITY sourceslist-list-format "deb http://us.archive.ubuntu.com/ubuntu &ubuntu-codename; main restricted
+deb http://security.ubuntu.com/ubuntu &ubuntu-codename;-security main restricted
+deb http://us.archive.ubuntu.com/ubuntu &ubuntu-codename;-updates main restricted">
+<!ENTITY sourceslist-sources-format "Types: deb
+URIs: http://us.archive.ubuntu.com/ubuntu
+Suites: &ubuntu-codename; &ubuntu-codename;-updates
+Components: main restricted
+
+Types: deb
+URIs: http://security.ubuntu.com/ubuntu
+Suites: &ubuntu-codename;-security
+Components: main restricted">