]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/tagfile.cc
sync TFRewrite*Order arrays with dpkg and dak
[apt.git] / apt-pkg / tagfile.cc
index bef3c76ba3c92c8c5c98e1342902109880f3bcac..2f7900d93693676fd02a993bf992896097ed7514 100644 (file)
@@ -21,6 +21,8 @@
 #include <string>
 #include <stdio.h>
 #include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
 
 #include <apti18n.h>
                                                                        /*}}}*/
@@ -45,16 +47,60 @@ public:
    unsigned long long Size;
 };
 
+class pkgTagSectionPrivate
+{
+public:
+   pkgTagSectionPrivate()
+   {
+   }
+   struct TagData {
+      unsigned int StartTag;
+      unsigned int EndTag;
+      unsigned int StartValue;
+      unsigned int NextInBucket;
+
+      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)                /*{{{*/
+{
+   /* This very simple hash function for the last 8 letters gives
+      very good performance on the debian package files */
+   if (Length > 8)
+   {
+    Text += (Length - 8);
+    Length = 8;
+   }
+   unsigned long Res = 0;
+   for (size_t i = 0; i < Length; ++i)
+      Res = ((unsigned long)(Text[i]) & 0xDF) ^ (Res << 1);
+   return Res & 0xFF;
+}
+                                                                       /*}}}*/
+
 // TagFile::pkgTagFile - Constructor                                   /*{{{*/
 // ---------------------------------------------------------------------
 /* */
 pkgTagFile::pkgTagFile(FileFd *pFd,unsigned long long Size)
+   : d(NULL)
+{
+   Init(pFd, Size);
+}
+
+void pkgTagFile::Init(FileFd *pFd,unsigned long long Size)
 {
    /* 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);
 
    if (d->Fd.IsOpen() == false)
@@ -83,7 +129,7 @@ pkgTagFile::~pkgTagFile()
 }
                                                                        /*}}}*/
 // TagFile::Offset - Return the current offset in the buffer           /*{{{*/
-unsigned long pkgTagFile::Offset()
+APT_PURE unsigned long pkgTagFile::Offset()
 {
    return d->iOffset;
 }
@@ -126,18 +172,23 @@ bool pkgTagFile::Resize(unsigned long long const newSize)
  */
 bool pkgTagFile::Step(pkgTagSection &Tag)
 {
-   while (Tag.Scan(d->Start,d->End - d->Start) == false)
+   if(Tag.Scan(d->Start,d->End - d->Start) == false)
    {
-      if (Fill() == false)
-        return false;
-      
-      if(Tag.Scan(d->Start,d->End - d->Start))
-        break;
+      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 (%d)"),
+                 d->Fd.Name().c_str(), 1);
 
-      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);
    }
+
    d->Start += Tag.size();
    d->iOffset += Tag.size();
 
@@ -207,7 +258,11 @@ bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long long Offset)
       unsigned long long Dist = Offset - d->iOffset;
       d->Start += Dist;
       d->iOffset += Dist;
-      return Step(Tag);
+      // 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);
    }
 
    // Reposition and reload..
@@ -227,8 +282,8 @@ bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long long Offset)
    if (Fill() == false)
       return false;
    
-   if (Tag.Scan(d->Start, d->End - d->Start) == false)
-      return _error->Error(_("Unable to parse package file %s (2)"),d->Fd.Name().c_str());
+   if (Tag.Scan(d->Start, d->End - d->Start, false) == false)
+      return _error->Error(_("Unable to parse package file %s (%d)"),d->Fd.Name().c_str(), 2);
    
    return true;
 }
@@ -236,28 +291,64 @@ bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long long Offset)
 // pkgTagSection::pkgTagSection - Constructor                          /*{{{*/
 // ---------------------------------------------------------------------
 /* */
+APT_IGNORE_DEPRECATED_PUSH
 pkgTagSection::pkgTagSection()
-   : Section(0), TagCount(0), d(NULL), Stop(0)
+   : Section(0), d(NULL), Stop(0)
 {
+   d = new pkgTagSectionPrivate();
+#if APT_PKG_ABI < 413
+   TagCount = 0;
    memset(&Indexes, 0, sizeof(Indexes));
+#endif
    memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
 }
+APT_IGNORE_DEPRECATED_POP
                                                                        /*}}}*/
 // TagSection::Scan - Scan for the end of the header information       /*{{{*/
-// ---------------------------------------------------------------------
-/* This looks for the first double new line in the data stream.
-   It also indexes the tags in the section. */
+#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)
+{
+   Section = Start;
    const char *End = Start + MaxLength;
-   Stop = Section = Start;
-   memset(AlphaIndexes,0,sizeof(AlphaIndexes));
+
+   if (Restart == false && 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);
+   }
+#if APT_PKG_ABI >= 413
+   unsigned int TagCount = d->Tags.size();
+#else
+   APT_IGNORE_DEPRECATED(TagCount = d->Tags.size();)
+#endif
 
    if (Stop == 0)
       return false;
 
-   TagCount = 0;
-   while (TagCount+1 < sizeof(Indexes)/sizeof(Indexes[0]) && Stop < End)
+   pkgTagSectionPrivate::TagData lastTagData(0);
+   lastTagData.EndTag = 0;
+   unsigned long lastTagHash = 0;
+   while (Stop < End)
    {
       TrimRecord(true,End);
 
@@ -269,12 +360,45 @@ bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength)
       // Start a new index and add it to the hash
       if (isspace(Stop[0]) == 0)
       {
-        Indexes[TagCount++] = Stop - Section;
-        AlphaIndexes[AlphaHash(Stop,End)] = TagCount;
+        // 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
+           d->Tags.push_back(lastTagData);
+        }
+
+        APT_IGNORE_DEPRECATED(++TagCount;)
+        lastTagData = pkgTagSectionPrivate::TagData(Stop - Section);
+        // find the colon separating tag and value
+        char const * Colon = (char const *) memchr(Stop, ':', End - Stop);
+        if (Colon == NULL)
+           return false;
+        // 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)
+           ;
+        ++EndTag;
+        lastTagData.EndTag = EndTag - Section;
+        lastTagHash = AlphaHash(Stop, EndTag - Stop);
+        // find the beginning of the value
+        Stop = Colon + 1;
+        for (; isspace(*Stop) != 0; ++Stop);
+        if (Stop >= End)
+           return false;
+        lastTagData.StartValue = Stop - Section;
       }
 
       Stop = (const char *)memchr(Stop,'\n',End - Stop);
-      
+
       if (Stop == 0)
         return false;
 
@@ -285,7 +409,22 @@ bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength)
       // Double newline marks the end of the record
       if (Stop+1 < End && Stop[1] == '\n')
       {
-        Indexes[TagCount] = Stop - Section;
+        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
+           d->Tags.push_back(lastTagData);
+        }
+
+        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);
         return true;
       }
@@ -314,8 +453,12 @@ void pkgTagSection::Trim()
    for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
 }
                                                                        /*}}}*/
-// TagSection::Exists - return True if a tag exists                    /*{{{*/
+// 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
 {
    unsigned int tmp;
    return Find(Tag, tmp);
@@ -326,73 +469,43 @@ bool pkgTagSection::Exists(const char* const Tag)
 /* This searches the section for a tag that matches the given string. */
 bool pkgTagSection::Find(const char *Tag,unsigned int &Pos) const
 {
-   unsigned int Length = strlen(Tag);
-   unsigned int I = AlphaIndexes[AlphaHash(Tag)];
-   if (I == 0)
+   size_t const Length = strlen(Tag);
+   unsigned int Bucket = AlphaIndexes[AlphaHash(Tag, Length)];
+   if (Bucket == 0)
       return false;
-   I--;
-   
-   for (unsigned int Counter = 0; Counter != TagCount; Counter++, 
-       I = (I+1)%TagCount)
+
+   for (; Bucket != 0; Bucket = d->Tags[Bucket - 1].NextInBucket)
    {
-      const char *St;
-      St = Section + Indexes[I];
-      if (strncasecmp(Tag,St,Length) != 0)
+      if ((d->Tags[Bucket - 1].EndTag - d->Tags[Bucket - 1].StartTag) != Length)
         continue;
 
-      // Make sure the colon is in the right place
-      const char *C = St + Length;
-      for (; isspace(*C) != 0; C++);
-      if (*C != ':')
+      char const * const St = Section + d->Tags[Bucket - 1].StartTag;
+      if (strncasecmp(Tag,St,Length) != 0)
         continue;
-      Pos = I;
+
+      Pos = Bucket - 1;
       return true;
    }
 
    Pos = 0;
    return false;
 }
-                                                                       /*}}}*/
-// TagSection::Find - Locate a tag                                     /*{{{*/
-// ---------------------------------------------------------------------
-/* This searches the section for a tag that matches the given string. */
 bool pkgTagSection::Find(const char *Tag,const char *&Start,
                         const char *&End) const
 {
-   unsigned int Length = strlen(Tag);
-   unsigned int I = AlphaIndexes[AlphaHash(Tag)];
-   if (I == 0)
+   unsigned int Pos;
+   if (Find(Tag, Pos) == false)
       return false;
-   I--;
-   
-   for (unsigned int Counter = 0; Counter != TagCount; Counter++, 
-       I = (I+1)%TagCount)
-   {
-      const char *St;
-      St = Section + Indexes[I];
-      if (strncasecmp(Tag,St,Length) != 0)
-        continue;
-      
-      // Make sure the colon is in the right place
-      const char *C = St + Length;
-      for (; isspace(*C) != 0; C++);
-      if (*C != ':')
-        continue;
 
-      // Strip off the gunk from the start end
-      Start = C;
-      End = Section + Indexes[I+1];
-      if (Start >= End)
-        return _error->Error("Internal parsing error");
-      
-      for (; (isspace(*Start) != 0 || *Start == ':') && Start < End; Start++);
-      for (; isspace(End[-1]) != 0 && End > Start; End--);
-      
-      return true;
-   }
-   
-   Start = End = 0;
-   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);
+
+   return true;
 }
                                                                        /*}}}*/
 // TagSection::FindS - Find a string                                   /*{{{*/
@@ -455,6 +568,17 @@ unsigned long long pkgTagSection::FindULL(const char *Tag, unsigned long long co
    return Result;
 }
                                                                        /*}}}*/
+// TagSection::FindB - Find boolean value                              /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool pkgTagSection::FindB(const char *Tag, bool const &Default) const
+{
+   const char *Start, *Stop;
+   if (Find(Tag, Start, Stop) == false)
+      return Default;
+   return StringToBool(string(Start, Stop));
+}
+                                                                       /*}}}*/
 // TagSection::FindFlag - Locate a yes/no type flag                    /*{{{*/
 // ---------------------------------------------------------------------
 /* The bits marked in Flag are masked on/off in Flags */
@@ -467,7 +591,7 @@ bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags,
       return true;
    return FindFlag(Flags, Flag, Start, Stop);
 }
-bool const pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
+bool pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
                                        char const* Start, char const* Stop)
 {
    switch (StringToBool(string(Start, Stop)))
@@ -487,71 +611,26 @@ bool const pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
    return true;
 }
                                                                        /*}}}*/
+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;
+   // the last element is just marking the end and isn't a real one
+   return d->Tags.size() - 1;
+}
+                                                                       /*}}}*/
+
+#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;
-   
 bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
               TFRewriteData *Rewrite)
 {
@@ -567,52 +646,54 @@ bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
    }
    
    // Write all all of the tags, in order.
-   for (unsigned int I = 0; Order[I] != 0; I++)
+   if (Order != NULL)
    {
-      bool Rewritten = false;
-      
-      // See if this is a field that needs to be rewritten
-      for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
+      for (unsigned int I = 0; Order[I] != 0; I++)
       {
-        if (strcasecmp(Rewrite[J].Tag,Order[I]) == 0)
-        {
-           Visited[J] |= 2;
-           if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
-           {
-              if (isspace(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);
-           }
+         bool Rewritten = false;
+         
+         // See if this is a field that needs to be rewritten
+         for (unsigned int J = 0; Rewrite != 0 && Rewrite[J].Tag != 0; J++)
+         {
+            if (strcasecmp(Rewrite[J].Tag,Order[I]) == 0)
+            {
+               Visited[J] |= 2;
+               if (Rewrite[J].Rewrite != 0 && Rewrite[J].Rewrite[0] != 0)
+               {
+                  if (isspace(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);
+               }
+               Rewritten = true;
+               break;
+            }
+         }
            
-           Rewritten = true;
-           break;
-        }
-      }      
-           
-      // See if it is in the fragment
-      unsigned Pos;
-      if (Tags.Find(Order[I],Pos) == false)
-        continue;
-      Visited[Pos] |= 1;
-
-      if (Rewritten == true)
-        continue;
+         // See if it is in the fragment
+         unsigned Pos;
+         if (Tags.Find(Order[I],Pos) == false)
+            continue;
+         Visited[Pos] |= 1;
+
+         if (Rewritten == true)
+            continue;
       
-      /* Write out this element, taking a moment to rewrite the tag
-         in case of changes of case. */
-      const char *Start;
-      const char *Stop;
-      Tags.Get(Start,Stop,Pos);
+         /* Write out this element, taking a moment to rewrite the tag
+            in case of changes of case. */
+         const char *Start;
+         const char *Stop;
+         Tags.Get(Start,Stop,Pos);
       
-      if (fputs(Order[I],Output) < 0)
-        return _error->Errno("fputs","IO Error to output");
-      Start += strlen(Order[I]);
-      if (fwrite(Start,Stop - Start,1,Output) != 1)
-        return _error->Errno("fwrite","IO Error to output");
-      if (Stop[-1] != '\n')
-        fprintf(Output,"\n");
-   }   
+         if (fputs(Order[I],Output) < 0)
+            return _error->Errno("fputs","IO Error to output");
+         Start += strlen(Order[I]);
+         if (fwrite(Start,Stop - Start,1,Output) != 1)
+            return _error->Errno("fwrite","IO Error to output");
+         if (Stop[-1] != '\n')
+            fprintf(Output,"\n");
+      }
+   }
 
    // Now write all the old tags that were missed.
    for (unsigned int I = 0; I != Tags.Count(); I++)
@@ -674,3 +755,5 @@ bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
    return true;
 }
                                                                        /*}}}*/
+
+pkgTagSection::~pkgTagSection() { delete d; }