]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/tagfile.cc
The entire concept of PendingError() is flawed :/.
[apt.git] / apt-pkg / tagfile.cc
index 590206f1720b771e678c0b09f8923056a26dbe05..1aa67cc6774f2cd97616324ffbf642e6028398eb 100644 (file)
 // Include Files                                                       /*{{{*/
 #include<config.h>
 
+#include <apt-pkg/mmap.h>
 #include <apt-pkg/tagfile.h>
+#include <apt-pkg/tagfile-keys.h>
 #include <apt-pkg/error.h>
 #include <apt-pkg/strutl.h>
 #include <apt-pkg/fileutl.h>
+#include <apt-pkg/string_view.h>
+
+#include <list>
 
 #include <string>
 #include <stdio.h>
                                                                        /*}}}*/
 
 using std::string;
+using APT::StringView;
 
-class pkgTagFilePrivate
+class APT_HIDDEN pkgTagFilePrivate                                     /*{{{*/
 {
 public:
-   pkgTagFilePrivate(FileFd *pFd, unsigned long long Size) : Fd(*pFd), Buffer(NULL),
-                                                            Start(NULL), End(NULL),
-                                                            Done(false), iOffset(0),
-                                                            Size(Size)
+   void Reset(FileFd * const pFd, pkgTagFile::Flags const pFlags)
    {
+      if (Map != NULL)
+         delete Map;
+      Map = NULL;
+      Buffer = NULL;
+      Fd = pFd;
+      Flags = pFlags;
+      Start = NULL;
+      End = NULL;
+      Done = false;
+      iOffset = 0;
    }
-   FileFd &Fd;
+
+   pkgTagFilePrivate(FileFd * const pFd, pkgTagFile::Flags const pFlags) : Map(NULL)
+   {
+      Reset(pFd, pFlags);
+   }
+   FileFd * Fd;
+   pkgTagFile::Flags Flags;
    char *Buffer;
    char *Start;
    char *End;
    bool Done;
    unsigned long long iOffset;
-   unsigned long long Size;
-};
+   MMap *Map;
 
-class pkgTagSectionPrivate
+   ~pkgTagFilePrivate()
+   {
+      if (Map != NULL)
+         delete Map;
+   }
+};
+                                                                       /*}}}*/
+class APT_HIDDEN pkgTagSectionPrivate                                  /*{{{*/
 {
 public:
    pkgTagSectionPrivate()
@@ -59,12 +84,13 @@ public:
       unsigned int StartValue;
       unsigned int NextInBucket;
 
-      TagData(unsigned int const StartTag) : StartTag(StartTag), EndTag(0), StartValue(0), NextInBucket(0) {}
+      explicit TagData(unsigned int const StartTag) : StartTag(StartTag), EndTag(0), StartValue(0), NextInBucket(0) {}
    };
    std::vector<TagData> Tags;
 };
+                                                                       /*}}}*/
 
-static unsigned long AlphaHash(const char *Text, size_t Length)                /*{{{*/
+static unsigned long BetaHash(const char *Text, size_t Length)         /*{{{*/
 {
    /* This very simple hash function for the last 8 letters gives
       very good performance on the debian package files */
@@ -76,55 +102,48 @@ static unsigned long AlphaHash(const char *Text, size_t Length)            /*{{{*/
    unsigned long Res = 0;
    for (size_t i = 0; i < Length; ++i)
       Res = ((unsigned long)(Text[i]) & 0xDF) ^ (Res << 1);
-   return Res & 0xFF;
+   return Res & 0x7F;
 }
                                                                        /*}}}*/
 
 // TagFile::pkgTagFile - Constructor                                   /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-pkgTagFile::pkgTagFile(FileFd *pFd,unsigned long long Size)
-   : d(NULL)
+pkgTagFile::pkgTagFile(FileFd * const pFd,pkgTagFile::Flags const pFlags)
+   : d(new pkgTagFilePrivate(pFd, pFlags))
 {
-   Init(pFd, Size);
+   Init(pFd, pFlags);
 }
-
-void pkgTagFile::Init(FileFd *pFd,unsigned long long Size)
+pkgTagFile::pkgTagFile(FileFd * const pFd)
+   : pkgTagFile(pFd, pkgTagFile::STRICT)
 {
-   /* The size is increased by 4 because if we start with the Size of the
-      filename we need to try to read 1 char more to see an EOF faster, 1
-      char the end-pointer can be on and maybe 2 newlines need to be added
-      to the end of the file -> 4 extra chars */
-   Size += 4;
-   if(d != NULL)
-   {
-      free(d->Buffer);
-      delete d;
-   }
-   d = new pkgTagFilePrivate(pFd, Size);
+}
+void pkgTagFile::Init(FileFd * const pFd, pkgTagFile::Flags const pFlags)
+{
+   d->Reset(pFd, pFlags);
 
-   if (d->Fd.IsOpen() == false)
-      d->Start = d->End = d->Buffer = 0;
-   else
-      d->Buffer = (char*)malloc(sizeof(char) * Size);
+   if (d->Fd->IsOpen() == false || d->Fd->Size() == 0)
+      _error->Discard();
+   else {
+      d->Map = new MMap(*d->Fd, MMap::ReadOnly);
+      d->Buffer = static_cast<char *>(d->Map->Data());
+   }
 
    if (d->Buffer == NULL)
       d->Done = true;
-   else
+   else {
       d->Done = false;
+      d->End = d->Buffer + d->Map->Size();
+   }
 
-   d->Start = d->End = d->Buffer;
-   d->iOffset = 0;
-   if (d->Done == false)
-      Fill();
+   d->Start = d->Buffer;
+}
+void pkgTagFile::Init(FileFd * const pFd)
+{
+   Init(pFd, pkgTagFile::STRICT);
 }
                                                                        /*}}}*/
 // TagFile::~pkgTagFile - Destructor                                   /*{{{*/
-// ---------------------------------------------------------------------
-/* */
 pkgTagFile::~pkgTagFile()
 {
-   free(d->Buffer);
    delete d;
 }
                                                                        /*}}}*/
@@ -134,36 +153,6 @@ APT_PURE unsigned long pkgTagFile::Offset()
    return d->iOffset;
 }
                                                                        /*}}}*/
-// TagFile::Resize - Resize the internal buffer                                /*{{{*/
-// ---------------------------------------------------------------------
-/* Resize the internal buffer (double it in size). Fail if a maximum size
- * size is reached.
- */
-bool pkgTagFile::Resize()
-{
-   // fail is the buffer grows too big
-   if(d->Size > 1024*1024+1)
-      return false;
-
-   return Resize(d->Size * 2);
-}
-bool pkgTagFile::Resize(unsigned long long const newSize)
-{
-   unsigned long long const EndSize = d->End - d->Start;
-
-   // get new buffer and use it
-   char* newBuffer = (char*)realloc(d->Buffer, sizeof(char) * newSize);
-   if (newBuffer == NULL)
-      return false;
-   d->Buffer = newBuffer;
-   d->Size = newSize;
-
-   // update the start/end pointers to the new buffer
-   d->Start = d->Buffer;
-   d->End = d->Start + EndSize;
-   return true;
-}
-                                                                       /*}}}*/
 // TagFile::Step - Advance to the next section                         /*{{{*/
 // ---------------------------------------------------------------------
 /* If the Section Scanner fails we refill the buffer and try again. 
@@ -172,77 +161,18 @@ bool pkgTagFile::Resize(unsigned long long const newSize)
  */
 bool pkgTagFile::Step(pkgTagSection &Tag)
 {
-   if(Tag.Scan(d->Start,d->End - d->Start) == false)
+   if(Tag.Scan(d->Start,d->End - d->Start,(d->Flags & SUPPORT_COMMENTS) != 0) == false)
    {
-      do
-      {
-        if (Fill() == false)
-           return false;
-
-        if(Tag.Scan(d->Start,d->End - d->Start, false))
-           break;
-
-        if (Resize() == false)
-           return _error->Error(_("Unable to parse package file %s (1)"),
-                 d->Fd.Name().c_str());
-
-      } while (Tag.Scan(d->Start,d->End - d->Start, false) == false);
+      if (d->Start == d->End)
+         return false;
+      else
+           return _error->Warning(_("Unable to parse package file %s (%d)"),
+                 d->Fd->Name().c_str(), 1);
    }
 
-   d->Start += Tag.size();
-   d->iOffset += Tag.size();
-
-   Tag.Trim();
-   return true;
-}
-                                                                       /*}}}*/
-// TagFile::Fill - Top up the buffer                                   /*{{{*/
-// ---------------------------------------------------------------------
-/* This takes the bit at the end of the buffer and puts it at the start
-   then fills the rest from the file */
-bool pkgTagFile::Fill()
-{
-   unsigned long long EndSize = d->End - d->Start;
-   unsigned long long Actual = 0;
-   
-   memmove(d->Buffer,d->Start,EndSize);
-   d->Start = d->Buffer;
-   d->End = d->Buffer + EndSize;
-   
-   if (d->Done == false)
-   {
-      // See if only a bit of the file is left
-      unsigned long long const dataSize = d->Size - ((d->End - d->Buffer) + 1);
-      if (d->Fd.Read(d->End, dataSize, &Actual) == false)
-        return false;
-      if (Actual != dataSize)
-        d->Done = true;
-      d->End += Actual;
-   }
-   
-   if (d->Done == true)
-   {
-      if (EndSize <= 3 && Actual == 0)
-        return false;
-      if (d->Size - (d->End - d->Buffer) < 4)
-        return true;
-      
-      // Append a double new line if one does not exist
-      unsigned int LineCount = 0;
-      for (const char *E = d->End - 1; E - d->End < 6 && (*E == '\n' || *E == '\r'); E--)
-        if (*E == '\n')
-           LineCount++;
-      if (LineCount < 2)
-      {
-        if ((unsigned)(d->End - d->Buffer) >= d->Size)
-           Resize(d->Size + 3);
-        for (; LineCount < 2; LineCount++)
-           *d->End++ = '\n';
-      }
-      
-      return true;
-   }
-   
+   size_t tagSize = Tag.size();
+   d->Start += tagSize;
+   d->iOffset += tagSize;
    return true;
 }
                                                                        /*}}}*/
@@ -252,40 +182,16 @@ bool pkgTagFile::Fill()
    that is there */
 bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long long Offset)
 {
-   // We are within a buffer space of the next hit..
-   if (Offset >= d->iOffset && d->iOffset + (d->End - d->Start) > Offset)
-   {
-      unsigned long long Dist = Offset - d->iOffset;
-      d->Start += Dist;
-      d->iOffset += Dist;
-      // if we have seen the end, don't ask for more
-      if (d->Done == true)
-        return Tag.Scan(d->Start, d->End - d->Start);
-      else
-        return Step(Tag);
-   }
+   unsigned int Size(d->Map->Size());
+   if (Offset >= Size)
+      return false;
 
    // Reposition and reload..
    d->iOffset = Offset;
    d->Done = false;
-   if (d->Fd.Seek(Offset) == false)
-      return false;
-   d->End = d->Start = d->Buffer;
-   
-   if (Fill() == false)
-      return false;
+   d->Start = d->Buffer + d->iOffset;
 
-   if (Tag.Scan(d->Start, d->End - d->Start) == true)
-      return true;
-   
-   // This appends a double new line (for the real eof handling)
-   if (Fill() == false)
-      return false;
-   
-   if (Tag.Scan(d->Start, d->End - d->Start, false) == false)
-      return _error->Error(_("Unable to parse package file %s (2)"),d->Fd.Name().c_str());
-   
-   return true;
+   return Step(Tag);
 }
                                                                        /*}}}*/
 // pkgTagSection::pkgTagSection - Constructor                          /*{{{*/
@@ -293,85 +199,61 @@ bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long long Offset)
 /* */
 APT_IGNORE_DEPRECATED_PUSH
 pkgTagSection::pkgTagSection()
-   : Section(0), d(NULL), Stop(0)
+   : Section(0), d(new pkgTagSectionPrivate()), Stop(0)
 {
-   d = new pkgTagSectionPrivate();
-#if APT_PKG_ABI < 413
-   TagCount = 0;
-   memset(&Indexes, 0, sizeof(Indexes));
-#endif
    memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
+   memset(&BetaIndexes, 0, sizeof(BetaIndexes));
 }
 APT_IGNORE_DEPRECATED_POP
                                                                        /*}}}*/
 // TagSection::Scan - Scan for the end of the header information       /*{{{*/
-#if APT_PKG_ABI < 413
-bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength)
-{
-   return Scan(Start, MaxLength, true);
-}
-#endif
-bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength, bool const Restart)
+bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength,bool const SupportComments)
 {
    Section = Start;
    const char *End = Start + MaxLength;
 
-   if (Restart == false && d->Tags.empty() == false)
+   Stop = Section;
+   if (d->Tags.empty() == false)
    {
-      Stop = Section + d->Tags.back().StartTag;
-      if (End <= Stop)
-        return false;
-      Stop = (const char *)memchr(Stop,'\n',End - Stop);
-      if (Stop == NULL)
-        return false;
-      ++Stop;
-   }
-   else
-   {
-      Stop = Section;
-      if (d->Tags.empty() == false)
-      {
-        memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
-        d->Tags.clear();
-      }
-      d->Tags.reserve(0x100);
+      memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
+      memset(&BetaIndexes, 0, sizeof(BetaIndexes));
+      d->Tags.clear();
    }
-#if APT_PKG_ABI >= 413
+   d->Tags.reserve(0x100);
+
    unsigned int TagCount = d->Tags.size();
-#else
-   APT_IGNORE_DEPRECATED(TagCount = d->Tags.size();)
-#endif
 
    if (Stop == 0)
       return false;
 
    pkgTagSectionPrivate::TagData lastTagData(0);
    lastTagData.EndTag = 0;
-   unsigned long lastTagHash = 0;
+   Key lastTagKey = Key::Unknown;
+   unsigned int lastTagHash = 0;
    while (Stop < End)
    {
-      TrimRecord(true,End);
+      TrimRecord(true,End,SupportComments);
 
       // this can happen when TrimRecord trims away the entire Record
       // (e.g. because it just contains comments)
       if(Stop == End)
-         return true;
+         goto end;
 
       // Start a new index and add it to the hash
-      if (isspace(Stop[0]) == 0)
+      if (isspace_ascii(Stop[0]) == 0)
       {
         // store the last found tag
         if (lastTagData.EndTag != 0)
         {
-           if (AlphaIndexes[lastTagHash] != 0)
-              lastTagData.NextInBucket = AlphaIndexes[lastTagHash];
-           APT_IGNORE_DEPRECATED_PUSH
-           AlphaIndexes[lastTagHash] = TagCount;
-#if APT_PKG_ABI < 413
-           if (d->Tags.size() < sizeof(Indexes)/sizeof(Indexes[0]))
-              Indexes[d->Tags.size()] = lastTagData.StartTag;
-#endif
-           APT_IGNORE_DEPRECATED_POP
+           if (lastTagKey != Key::Unknown) {
+              AlphaIndexes[static_cast<size_t>(lastTagKey)] = TagCount;
+           } else {
+              if (BetaIndexes[lastTagHash] != 0)
+                 lastTagData.NextInBucket = BetaIndexes[lastTagHash];
+              APT_IGNORE_DEPRECATED_PUSH
+              BetaIndexes[lastTagHash] = TagCount;
+              APT_IGNORE_DEPRECATED_POP
+           }
            d->Tags.push_back(lastTagData);
         }
 
@@ -384,23 +266,27 @@ bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength, bool const R
         // find the end of the tag (which might or might not be the colon)
         char const * EndTag = Colon;
         --EndTag;
-        for (; EndTag > Stop && isspace(*EndTag) != 0; --EndTag)
+        for (; EndTag > Stop && isspace_ascii(*EndTag) != 0; --EndTag)
            ;
         ++EndTag;
         lastTagData.EndTag = EndTag - Section;
-        lastTagHash = AlphaHash(Stop, EndTag - Stop);
+        lastTagKey = pkgTagHash(Stop, EndTag - Stop);
+        if (lastTagKey == Key::Unknown)
+           lastTagHash = BetaHash(Stop, EndTag - Stop);
         // find the beginning of the value
         Stop = Colon + 1;
-        for (; isspace(*Stop) != 0; ++Stop);
-        if (Stop >= End)
-           return false;
+        for (; Stop < End && isspace_ascii(*Stop) != 0; ++Stop)
+           if (*Stop == '\n' && (Stop+1 == End || Stop[1] != ' '))
+              break;
         lastTagData.StartValue = Stop - Section;
       }
 
       Stop = (const char *)memchr(Stop,'\n',End - Stop);
 
-      if (Stop == 0)
-        return false;
+      if (Stop == 0) {
+         Stop = End;
+         goto end;
+      }
 
       for (; Stop+1 < End && Stop[1] == '\r'; Stop++)
          /* nothing */
@@ -408,57 +294,49 @@ bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength, bool const R
 
       // Double newline marks the end of the record
       if (Stop+1 < End && Stop[1] == '\n')
-      {
+      end: {
         if (lastTagData.EndTag != 0)
         {
-           if (AlphaIndexes[lastTagHash] != 0)
-              lastTagData.NextInBucket = AlphaIndexes[lastTagHash];
-           APT_IGNORE_DEPRECATED(AlphaIndexes[lastTagHash] = TagCount;)
-#if APT_PKG_ABI < 413
-           APT_IGNORE_DEPRECATED(Indexes[d->Tags.size()] = lastTagData.StartTag;)
-#endif
+           if (lastTagKey != Key::Unknown) {
+              AlphaIndexes[static_cast<size_t>(lastTagKey)] = TagCount;
+           } else {
+              if (BetaIndexes[lastTagHash] != 0)
+                 lastTagData.NextInBucket = BetaIndexes[lastTagHash];
+              APT_IGNORE_DEPRECATED(BetaIndexes[lastTagHash] = TagCount;)
+           }
            d->Tags.push_back(lastTagData);
         }
 
+         if (d->Tags.empty())
+            return false;
+
         pkgTagSectionPrivate::TagData const td(Stop - Section);
-#if APT_PKG_ABI < 413
-        APT_IGNORE_DEPRECATED(Indexes[d->Tags.size()] = td.StartTag;)
-#endif
         d->Tags.push_back(td);
-        TrimRecord(false,End);
+        TrimRecord(false,End,SupportComments);
         return true;
       }
       
       Stop++;
    }
 
-   return false;
+   goto end;
 }
                                                                        /*}}}*/
 // TagSection::TrimRecord - Trim off any garbage before/after a record /*{{{*/
 // ---------------------------------------------------------------------
 /* There should be exactly 2 newline at the end of the record, no more. */
-void pkgTagSection::TrimRecord(bool BeforeRecord, const char*& End)
-{
-   if (BeforeRecord == true)
-      return;
-   for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r'); Stop++);
-}
-                                                                       /*}}}*/
-// TagSection::Trim - Trim off any trailing garbage                    /*{{{*/
-// ---------------------------------------------------------------------
-/* There should be exactly 1 newline at the end of the buffer, no more. */
-void pkgTagSection::Trim()
-{
-   for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
+void pkgTagSection::TrimRecord(bool BeforeRecord, const char*& End, bool SupportComments)
+{ trim:
+   if (BeforeRecord == false)
+      for (; Stop < End && (Stop[0] == '\n' || Stop[0] == '\r'); Stop++);
+   if (SupportComments && Stop < End && Stop[0] == '#') {
+      Stop = (const char*) memchr(Stop,'\n',End-Stop) ?: End;
+      goto trim;
+   }
 }
                                                                        /*}}}*/
 // TagSection::Exists - return True if a tag exists                    /*{{{*/
-#if APT_PKG_ABI >= 413
-bool pkgTagSection::Exists(const char* const Tag) const
-#else
-bool pkgTagSection::Exists(const char* const Tag)
-#endif
+bool pkgTagSection::Exists(StringView Tag) const
 {
    unsigned int tmp;
    return Find(Tag, tmp);
@@ -467,10 +345,21 @@ bool pkgTagSection::Exists(const char* const Tag)
 // TagSection::Find - Locate a tag                                     /*{{{*/
 // ---------------------------------------------------------------------
 /* This searches the section for a tag that matches the given string. */
-bool pkgTagSection::Find(const char *Tag,unsigned int &Pos) const
+bool pkgTagSection::Find(Key key,unsigned int &Pos) const
+{
+   auto Bucket = AlphaIndexes[static_cast<size_t>(key)];
+   Pos = Bucket - 1;
+   return Bucket != 0;
+}
+bool pkgTagSection::Find(StringView TagView,unsigned int &Pos) const
 {
-   size_t const Length = strlen(Tag);
-   unsigned int Bucket = AlphaIndexes[AlphaHash(Tag, Length)];
+   const char * const Tag = TagView.data();
+   size_t const Length = TagView.length();
+   auto key = pkgTagHash(Tag, Length);
+   if (key != Key::Unknown)
+      return Find(key, Pos);
+
+   unsigned int Bucket = BetaIndexes[BetaHash(Tag, Length)];
    if (Bucket == 0)
       return false;
 
@@ -490,44 +379,83 @@ bool pkgTagSection::Find(const char *Tag,unsigned int &Pos) const
    Pos = 0;
    return false;
 }
-bool pkgTagSection::Find(const char *Tag,const char *&Start,
+
+bool pkgTagSection::FindInternal(unsigned int Pos, const char *&Start,
                         const char *&End) const
 {
-   unsigned int Pos;
-   if (Find(Tag, Pos) == false)
-      return false;
-
    Start = Section + d->Tags[Pos].StartValue;
    // Strip off the gunk from the end
    End = Section + d->Tags[Pos + 1].StartTag;
    if (unlikely(Start > End))
       return _error->Error("Internal parsing error");
 
-   for (; isspace(End[-1]) != 0 && End > Start; --End);
+   for (; End > Start && isspace_ascii(End[-1]) != 0; --End);
 
    return true;
+}
+bool pkgTagSection::Find(StringView Tag,const char *&Start,
+                        const char *&End) const
+{
+   unsigned int Pos;
+   return Find(Tag, Pos) && FindInternal(Pos, Start, End);
+}
+bool pkgTagSection::Find(Key key,const char *&Start,
+                        const char *&End) const
+{
+   unsigned int Pos;
+   return Find(key, Pos) && FindInternal(Pos, Start, End);
 }
                                                                        /*}}}*/
 // TagSection::FindS - Find a string                                   /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-string pkgTagSection::FindS(const char *Tag) const
+StringView pkgTagSection::Find(StringView Tag) const
 {
    const char *Start;
    const char *End;
    if (Find(Tag,Start,End) == false)
-      return string();
-   return string(Start,End);      
+      return StringView();
+   return StringView(Start, End - Start);
+}
+StringView pkgTagSection::Find(Key key) const
+{
+   const char *Start;
+   const char *End;
+   if (Find(key,Start,End) == false)
+      return StringView();
+   return StringView(Start, End - Start);
+}
+                                                                       /*}}}*/
+// TagSection::FindRawS - Find a string                                        /*{{{*/
+StringView pkgTagSection::FindRawInternal(unsigned int Pos) const
+{
+   char const *Start = (char const *) memchr(Section + d->Tags[Pos].EndTag, ':', d->Tags[Pos].StartValue - d->Tags[Pos].EndTag);
+   ++Start;
+   char const *End = Section + d->Tags[Pos + 1].StartTag;
+   if (unlikely(Start > End))
+      return "";
+
+   for (; isspace_ascii(End[-1]) != 0 && End > Start; --End);
+
+   return StringView(Start, End - Start);
+}
+StringView pkgTagSection::FindRaw(StringView Tag) const
+{
+   unsigned int Pos;
+   return Find(Tag, Pos) ? FindRawInternal(Pos) : "";
+}
+StringView pkgTagSection::FindRaw(Key key) const
+{
+   unsigned int Pos;
+   return Find(key, Pos) ? FindRawInternal(Pos) : "";
 }
                                                                        /*}}}*/
 // TagSection::FindI - Find an integer                                 /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-signed int pkgTagSection::FindI(const char *Tag,signed long Default) const
+signed int pkgTagSection::FindIInternal(unsigned int Pos,signed long Default) const
 {
    const char *Start;
    const char *Stop;
-   if (Find(Tag,Start,Stop) == false)
+   if (FindInternal(Pos,Start,Stop) == false)
       return Default;
 
    // Copy it into a temp buffer so we can use strtol
@@ -536,22 +464,40 @@ signed int pkgTagSection::FindI(const char *Tag,signed long Default) const
       return Default;
    strncpy(S,Start,Stop-Start);
    S[Stop - Start] = 0;
-   
+
+   errno = 0;
    char *End;
    signed long Result = strtol(S,&End,10);
+   if (errno == ERANGE ||
+       Result < std::numeric_limits<int>::min() || Result > std::numeric_limits<int>::max()) {
+      errno = ERANGE;
+      _error->Error(_("Cannot convert %s to integer: out of range"), S);
+   }
    if (S == End)
       return Default;
    return Result;
+}
+signed int pkgTagSection::FindI(Key key,signed long Default) const
+{
+   unsigned int Pos;
+
+   return Find(key, Pos) ? FindIInternal(Pos) : Default;
+}
+signed int pkgTagSection::FindI(StringView Tag,signed long Default) const
+{
+   unsigned int Pos;
+
+   return Find(Tag, Pos) ? FindIInternal(Pos, Default) : Default;
 }
                                                                        /*}}}*/
 // TagSection::FindULL - Find an unsigned long long integer            /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-unsigned long long pkgTagSection::FindULL(const char *Tag, unsigned long long const &Default) const
+unsigned long long pkgTagSection::FindULLInternal(unsigned int Pos, unsigned long long const &Default) const
 {
    const char *Start;
    const char *Stop;
-   if (Find(Tag,Start,Stop) == false)
+   if (FindInternal(Pos,Start,Stop) == false)
       return Default;
 
    // Copy it into a temp buffer so we can use strtoull
@@ -566,31 +512,109 @@ unsigned long long pkgTagSection::FindULL(const char *Tag, unsigned long long co
    if (S == End)
       return Default;
    return Result;
+}
+unsigned long long pkgTagSection::FindULL(Key key, unsigned long long const &Default) const
+{
+   unsigned int Pos;
+
+   return Find(key, Pos) ? FindULLInternal(Pos, Default) : Default;
+}
+unsigned long long pkgTagSection::FindULL(StringView Tag, unsigned long long const &Default) const
+{
+   unsigned int Pos;
+
+   return Find(Tag, Pos) ? FindULLInternal(Pos, Default) : Default;
 }
                                                                        /*}}}*/
 // TagSection::FindB - Find boolean value                              /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-bool pkgTagSection::FindB(const char *Tag, bool const &Default) const
+bool pkgTagSection::FindBInternal(unsigned int Pos, bool Default) const
 {
    const char *Start, *Stop;
-   if (Find(Tag, Start, Stop) == false)
+   if (FindInternal(Pos, Start, Stop) == false)
       return Default;
    return StringToBool(string(Start, Stop));
+}
+bool pkgTagSection::FindB(Key key, bool Default) const
+{
+   unsigned int Pos;
+   return Find(key, Pos) ? FindBInternal(Pos, Default): Default;
+}
+bool pkgTagSection::FindB(StringView Tag, bool Default) const
+{
+   unsigned int Pos;
+   return Find(Tag, Pos) ? FindBInternal(Pos, Default) : Default;
 }
                                                                        /*}}}*/
 // TagSection::FindFlag - Locate a yes/no type flag                    /*{{{*/
 // ---------------------------------------------------------------------
 /* The bits marked in Flag are masked on/off in Flags */
-bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags,
+bool pkgTagSection::FindFlagInternal(unsigned int Pos, uint8_t &Flags,
+                            uint8_t const Flag) const
+{
+   const char *Start;
+   const char *Stop;
+   if (FindInternal(Pos,Start,Stop) == false)
+      return true;
+   return FindFlag(Flags, Flag, Start, Stop);
+}
+bool pkgTagSection::FindFlag(Key key, uint8_t &Flags,
+                            uint8_t const Flag) const
+{
+   unsigned int Pos;
+   if (Find(key,Pos) == false)
+      return true;
+   return FindFlagInternal(Pos, Flags, Flag);
+}
+bool pkgTagSection::FindFlag(StringView Tag, uint8_t &Flags,
+                            uint8_t const Flag) const
+{
+   unsigned int Pos;
+   if (Find(Tag,Pos) == false)
+      return true;
+   return FindFlagInternal(Pos, Flags, Flag);
+}
+bool pkgTagSection::FindFlag(uint8_t &Flags, uint8_t const Flag,
+                                       char const* const Start, char const* const Stop)
+{
+   switch (StringToBool(string(Start, Stop)))
+   {
+      case 0:
+      Flags &= ~Flag;
+      return true;
+
+      case 1:
+      Flags |= Flag;
+      return true;
+
+      default:
+      _error->Warning("Unknown flag value: %s",string(Start,Stop).c_str());
+      return true;
+   }
+   return true;
+}
+bool pkgTagSection::FindFlagInternal(unsigned int Pos,unsigned long &Flags,
                             unsigned long Flag) const
 {
    const char *Start;
    const char *Stop;
-   if (Find(Tag,Start,Stop) == false)
+   if (FindInternal(Pos,Start,Stop) == false)
       return true;
    return FindFlag(Flags, Flag, Start, Stop);
 }
+bool pkgTagSection::FindFlag(Key key,unsigned long &Flags,
+                            unsigned long Flag) const
+{
+   unsigned int Pos;
+   return Find(key, Pos) ? FindFlagInternal(Pos, Flags, Flag) : true;
+}
+bool pkgTagSection::FindFlag(StringView Tag,unsigned long &Flags,
+                            unsigned long Flag) const
+{
+   unsigned int Pos;
+   return Find(Tag, Pos) ? FindFlagInternal(Pos, Flags, Flag) : true;
+}
 bool pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
                                        char const* Start, char const* Stop)
 {
@@ -611,11 +635,12 @@ bool pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
    return true;
 }
                                                                        /*}}}*/
-void pkgTagSection::Get(const char *&Start,const char *&Stop,unsigned int I) const
+void pkgTagSection::Get(const char *&Start,const char *&Stop,unsigned int I) const/*{{{*/
 {
    Start = Section + d->Tags[I].StartTag;
    Stop = Section + d->Tags[I+1].StartTag;
 }
+                                                                       /*}}}*/
 APT_PURE unsigned int pkgTagSection::Count() const {                   /*{{{*/
    if (d->Tags.empty() == true)
       return 0;
@@ -623,71 +648,141 @@ APT_PURE unsigned int pkgTagSection::Count() const {                     /*{{{*/
    return d->Tags.size() - 1;
 }
                                                                        /*}}}*/
+// TagSection::Write - Ordered (re)writing of fields                   /*{{{*/
+pkgTagSection::Tag pkgTagSection::Tag::Remove(std::string const &Name)
+{
+   return Tag(REMOVE, Name, "");
+}
+pkgTagSection::Tag pkgTagSection::Tag::Rename(std::string const &OldName, std::string const &NewName)
+{
+   return Tag(RENAME, OldName, NewName);
+}
+pkgTagSection::Tag pkgTagSection::Tag::Rewrite(std::string const &Name, std::string const &Data)
+{
+   if (Data.empty() == true)
+      return Tag(REMOVE, Name, "");
+   else
+      return Tag(REWRITE, Name, Data);
+}
+static bool WriteTag(FileFd &File, std::string Tag, StringView Value)
+{
+   if (Value.empty() || isspace_ascii(Value[0]) != 0)
+      Tag.append(":");
+   else
+      Tag.append(": ");
+   Tag.append(Value.data(), Value.length());
+   Tag.append("\n");
+   return File.Write(Tag.c_str(), Tag.length());
+}
+static bool RewriteTags(FileFd &File, pkgTagSection const * const This, char const * const Tag,
+      std::vector<pkgTagSection::Tag>::const_iterator &R,
+      std::vector<pkgTagSection::Tag>::const_iterator const &REnd)
+{
+   size_t const TagLen = strlen(Tag);
+   for (; R != REnd; ++R)
+   {
+      std::string data;
+      if (R->Name.length() == TagLen && strncasecmp(R->Name.c_str(), Tag, R->Name.length()) == 0)
+      {
+        if (R->Action != pkgTagSection::Tag::REWRITE)
+           break;
+        data = R->Data;
+      }
+      else if(R->Action == pkgTagSection::Tag::RENAME && R->Data.length() == TagLen &&
+           strncasecmp(R->Data.c_str(), Tag, R->Data.length()) == 0)
+        data = This->FindRaw(R->Name.c_str()).to_string();
+      else
+        continue;
+
+      return WriteTag(File, Tag, data);
+   }
+   return true;
+}
+bool pkgTagSection::Write(FileFd &File, char const * const * const Order, std::vector<Tag> const &Rewrite) const
+{
+   // first pass: Write everything we have an order for
+   if (Order != NULL)
+   {
+      for (unsigned int I = 0; Order[I] != 0; ++I)
+      {
+        std::vector<Tag>::const_iterator R = Rewrite.begin();
+        if (RewriteTags(File, this, Order[I], R, Rewrite.end()) == false)
+           return false;
+        if (R != Rewrite.end())
+           continue;
+
+        if (Exists(Order[I]) == false)
+           continue;
+
+        if (WriteTag(File, Order[I], FindRaw(Order[I])) == false)
+           return false;
+      }
+   }
+   // second pass: See if we have tags which aren't ordered
+   if (d->Tags.empty() == false)
+   {
+      for (std::vector<pkgTagSectionPrivate::TagData>::const_iterator T = d->Tags.begin(); T != d->Tags.end() - 1; ++T)
+      {
+        char const * const fieldname = Section + T->StartTag;
+        size_t fieldnamelen = T->EndTag - T->StartTag;
+        if (Order != NULL)
+        {
+           unsigned int I = 0;
+           for (; Order[I] != 0; ++I)
+           {
+              if (fieldnamelen == strlen(Order[I]) && strncasecmp(fieldname, Order[I], fieldnamelen) == 0)
+                 break;
+           }
+           if (Order[I] != 0)
+              continue;
+        }
+
+        std::string const name(fieldname, fieldnamelen);
+        std::vector<Tag>::const_iterator R = Rewrite.begin();
+        if (RewriteTags(File, this, name.c_str(), R, Rewrite.end()) == false)
+           return false;
+        if (R != Rewrite.end())
+           continue;
+
+        if (WriteTag(File, name, FindRaw(name)) == false)
+           return false;
+      }
+   }
+   // last pass: see if there are any rewrites remaining we haven't done yet
+   for (std::vector<Tag>::const_iterator R = Rewrite.begin(); R != Rewrite.end(); ++R)
+   {
+      if (R->Action == Tag::REMOVE)
+        continue;
+      std::string const name = ((R->Action == Tag::RENAME) ? R->Data : R->Name);
+      if (Exists(name.c_str()))
+        continue;
+      if (Order != NULL)
+      {
+        unsigned int I = 0;
+        for (; Order[I] != 0; ++I)
+        {
+           if (strncasecmp(name.c_str(), Order[I], name.length()) == 0 && name.length() == strlen(Order[I]))
+              break;
+        }
+        if (Order[I] != 0)
+           continue;
+      }
+
+      if (WriteTag(File, name, ((R->Action == Tag::RENAME) ? FindRaw(R->Name) : R->Data)) == false)
+        return false;
+   }
+   return true;
+}
+                                                                       /*}}}*/
+
+#include "tagfile-order.c"
+
 // TFRewrite - Rewrite a control record                                        /*{{{*/
 // ---------------------------------------------------------------------
 /* This writes the control record to stdout rewriting it as necessary. The
    override map item specificies the rewriting rules to follow. This also
    takes the time to sort the feild list. */
-
-/* The order of this list is taken from dpkg source lib/parse.c the fieldinfos
-   array. */
-static const char *iTFRewritePackageOrder[] = {
-                          "Package",
-                          "Essential",
-                          "Status",
-                          "Priority",
-                          "Section",
-                          "Installed-Size",
-                          "Maintainer",
-                          "Original-Maintainer",
-                          "Architecture",
-                          "Source",
-                          "Version",
-                           "Revision",         // Obsolete
-                           "Config-Version",   // Obsolete
-                          "Replaces",
-                          "Provides",
-                          "Depends",
-                          "Pre-Depends",
-                          "Recommends",
-                          "Suggests",
-                          "Conflicts",
-                          "Breaks",
-                          "Conffiles",
-                          "Filename",
-                          "Size",
-                          "MD5sum",
-                          "SHA1",
-                          "SHA256",
-                          "SHA512",
-                           "MSDOS-Filename",   // Obsolete
-                          "Description",
-                          0};
-static const char *iTFRewriteSourceOrder[] = {"Package",
-                                      "Source",
-                                      "Binary",
-                                      "Version",
-                                      "Priority",
-                                      "Section",
-                                      "Maintainer",
-                                     "Original-Maintainer",
-                                      "Build-Depends",
-                                      "Build-Depends-Indep",
-                                      "Build-Conflicts",
-                                      "Build-Conflicts-Indep",
-                                      "Architecture",
-                                      "Standards-Version",
-                                      "Format",
-                                      "Directory",
-                                      "Files",
-                                      0};   
-
-/* Two levels of initialization are used because gcc will set the symbol
-   size of an array to the length of the array, causing dynamic relinking 
-   errors. Doing this makes the symbol size constant */
-const char **TFRewritePackageOrder = iTFRewritePackageOrder;
-const char **TFRewriteSourceOrder = iTFRewriteSourceOrder;
-   
+APT_IGNORE_DEPRECATED_PUSH
 bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
               TFRewriteData *Rewrite)
 {
@@ -717,7 +812,7 @@ bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
                Visited[J] |= 2;
                if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
                {
-                  if (isspace(Rewrite[J].Rewrite[0]))
+                  if (isspace_ascii(Rewrite[J].Rewrite[0]))
                      fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
                   else
                      fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
@@ -729,7 +824,7 @@ bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
            
          // See if it is in the fragment
          unsigned Pos;
-         if (Tags.Find(Order[I],Pos) == false)
+         if (Tags.Find(StringView(Order[I]),Pos) == false)
             continue;
          Visited[Pos] |= 1;
 
@@ -773,7 +868,7 @@ bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
            Visited[J] |= 2;
            if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
            {
-              if (isspace(Rewrite[J].Rewrite[0]))
+              if (isspace_ascii(Rewrite[J].Rewrite[0]))
                  fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
               else
                  fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
@@ -802,7 +897,7 @@ bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
       
       if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
       {
-        if (isspace(Rewrite[J].Rewrite[0]))
+        if (isspace_ascii(Rewrite[J].Rewrite[0]))
            fprintf(Output,"%s:%s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
         else
            fprintf(Output,"%s: %s\n",Rewrite[J].NewTag,Rewrite[J].Rewrite);
@@ -811,6 +906,7 @@ bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
       
    return true;
 }
+APT_IGNORE_DEPRECATED_POP
                                                                        /*}}}*/
 
 pkgTagSection::~pkgTagSection() { delete d; }