]> git.saurik.com Git - apt.git/commitdiff
implement a more c++-style TFRewrite alternative
authorDavid Kalnischkies <david@kalnischkies.de>
Sun, 10 May 2015 20:53:15 +0000 (22:53 +0200)
committerDavid Kalnischkies <david@kalnischkies.de>
Mon, 11 May 2015 15:22:32 +0000 (17:22 +0200)
TFRewrite is okay, but it has obscure limitations (256 Tags), even more
obscure bugs (order for renames is defined by the old name) and the
interface is very c-style encouraging bad usage like we do it in
apt-ftparchive passing massive amounts of c_str() from std::string in.

The old-style is marked as deprecated accordingly. The next commit will
fix all places in the apt code to not use the old-style anymore.

apt-pkg/tagfile.cc
apt-pkg/tagfile.h
test/libapt/tagfile_test.cc

index 2f7900d93693676fd02a993bf992896097ed7514..5ff495fbdc5797322b7ee155c8e5bfc4076a47ff 100644 (file)
@@ -509,8 +509,6 @@ bool pkgTagSection::Find(const char *Tag,const char *&Start,
 }
                                                                        /*}}}*/
 // TagSection::FindS - Find a string                                   /*{{{*/
-// ---------------------------------------------------------------------
-/* */
 string pkgTagSection::FindS(const char *Tag) const
 {
    const char *Start;
@@ -520,6 +518,24 @@ string pkgTagSection::FindS(const char *Tag) const
    return string(Start,End);      
 }
                                                                        /*}}}*/
+// TagSection::FindRawS - Find a string                                        /*{{{*/
+string pkgTagSection::FindRawS(const char *Tag) const
+{
+   unsigned int Pos;
+   if (Find(Tag, Pos) == false)
+      return "";
+
+   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(End[-1]) != 0 && End > Start; --End);
+
+   return std::string(Start, End - Start);
+}
+                                                                       /*}}}*/
 // TagSection::FindI - Find an integer                                 /*{{{*/
 // ---------------------------------------------------------------------
 /* */
@@ -623,6 +639,132 @@ 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, std::string const &Value)
+{
+   if (Value.empty() || isspace(Value[0]) != 0)
+      Tag.append(":");
+   else
+      Tag.append(": ");
+   Tag.append(Value);
+   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->FindRawS(R->Name.c_str());
+      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], FindRawS(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, FindRawS(name.c_str())) == 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) ? FindRawS(R->Name.c_str()) : R->Data)) == false)
+        return false;
+   }
+   return true;
+}
+                                                                       /*}}}*/
 
 #include "tagfile-order.c"
 
@@ -631,6 +773,7 @@ APT_PURE unsigned int pkgTagSection::Count() const {                        /*{{{*/
 /* 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. */
+APT_IGNORE_DEPRECATED_PUSH
 bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
               TFRewriteData *Rewrite)
 {
@@ -754,6 +897,7 @@ bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
       
    return true;
 }
+APT_IGNORE_DEPRECATED_POP
                                                                        /*}}}*/
 
 pkgTagSection::~pkgTagSection() { delete d; }
index d09e7046c23f7d9156a5b3eda5a77a23564a0557..1189545419caa3733c35f2eab50431598fe295cd 100644 (file)
@@ -54,13 +54,14 @@ class pkgTagSection
    const char *Stop;
 
    public:
-   
+
    inline bool operator ==(const pkgTagSection &rhs) {return Section == rhs.Section;};
    inline bool operator !=(const pkgTagSection &rhs) {return Section != rhs.Section;};
-   
+
    bool Find(const char *Tag,const char *&Start, const char *&End) const;
    bool Find(const char *Tag,unsigned int &Pos) const;
    std::string FindS(const char *Tag) const;
+   std::string FindRawS(const char *Tag) const;
    signed int FindI(const char *Tag,signed long Default = 0) const;
    bool FindB(const char *Tag, bool const &Default = false) const;
    unsigned long long FindULL(const char *Tag, unsigned long long const &Default = 0) const;
@@ -115,9 +116,32 @@ class pkgTagSection
       Start = Section;
       Stop = this->Stop;
    };
-   
+
    pkgTagSection();
    virtual ~pkgTagSection();
+
+   struct Tag
+   {
+      enum ActionType { REMOVE, RENAME, REWRITE } Action;
+      std::string Name;
+      std::string Data;
+
+      static Tag Remove(std::string const &Name);
+      static Tag Rename(std::string const &OldName, std::string const &NewName);
+      static Tag Rewrite(std::string const &Name, std::string const &Data);
+      private:
+      Tag(ActionType const Action, std::string const &Name, std::string const &Data) :
+        Action(Action), Name(Name), Data(Data) {}
+   };
+
+   /** Write this section (with optional rewrites) to a file
+    *
+    * @param File to write the section to
+    * @param Order in which tags should appear in the file
+    * @param Rewrite is a set of tags to be renamed, rewitten and/or removed
+    * @return \b true if successful, otherwise \b false
+    */
+   bool Write(FileFd &File, char const * const * const Order = NULL, std::vector<Tag> const &Rewrite = std::vector<Tag>()) const;
 };
 
 class pkgTagFilePrivate;
@@ -141,20 +165,19 @@ class pkgTagFile
    virtual ~pkgTagFile();
 };
 
-/* This is the list of things to rewrite. The rewriter
-   goes through and changes or adds each of these headers
-   to suit. A zero forces the header to be erased, an empty string
-   causes the old value to be used. (rewrite rule ignored) */
-struct TFRewriteData
+extern const char **TFRewritePackageOrder;
+extern const char **TFRewriteSourceOrder;
+
+// Use pkgTagSection::Tag and pkgTagSection::Write() instead
+APT_IGNORE_DEPRECATED_PUSH
+struct APT_DEPRECATED TFRewriteData
 {
    const char *Tag;
    const char *Rewrite;
    const char *NewTag;
 };
-extern const char **TFRewritePackageOrder;
-extern const char **TFRewriteSourceOrder;
-
-bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
+APT_DEPRECATED bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
               TFRewriteData *Rewrite);
+APT_IGNORE_DEPRECATED_POP
 
 #endif
index df618ea1617183f64e5963b5e2b3fb5b870edd71..d7030f41a2723a8144925b7e0b6f8524d11224fc 100644 (file)
@@ -34,6 +34,12 @@ TEST(TagFileTest,SingleField)
    EXPECT_FALSE(section.Exists("FieldB-12345678"));
    // There is only one section in this tag file
    EXPECT_FALSE(tfile.Step(section));
+
+   // Now we scan an empty section to test reset
+   ASSERT_TRUE(section.Scan("\n\n", 2, true));
+   EXPECT_EQ(0, section.Count());
+   EXPECT_FALSE(section.Exists("FieldA-12345678"));
+   EXPECT_FALSE(section.Exists("FieldB-12345678"));
 }
 
 TEST(TagFileTest,MultipleSections)