]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/tagfile.cc
deal with empty values properly in deb822 parser
[apt.git] / apt-pkg / tagfile.cc
index 590206f1720b771e678c0b09f8923056a26dbe05..a0b64f9ca812f95686366dffbe9cf1d57118d755 100644 (file)
@@ -32,19 +32,36 @@ using std::string;
 class 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, unsigned long long const pSize)
    {
+      if (Buffer != NULL)
+        free(Buffer);
+      Buffer = NULL;
+      Fd = pFd;
+      Start = NULL;
+      End = NULL;
+      Done = false;
+      iOffset = 0;
+      Size = pSize;
    }
-   FileFd &Fd;
+
+   pkgTagFilePrivate(FileFd * const pFd, unsigned long long const Size) : Buffer(NULL)
+   {
+      Reset(pFd, Size);
+   }
+   FileFd * Fd;
    char *Buffer;
    char *Start;
    char *End;
    bool Done;
    unsigned long long iOffset;
    unsigned long long Size;
+
+   ~pkgTagFilePrivate()
+   {
+      if (Buffer != NULL)
+        free(Buffer);
+   }
 };
 
 class pkgTagSectionPrivate
@@ -59,7 +76,7 @@ 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;
 };
@@ -83,27 +100,21 @@ static unsigned long AlphaHash(const char *Text, size_t Length)            /*{{{*/
 // TagFile::pkgTagFile - Constructor                                   /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-pkgTagFile::pkgTagFile(FileFd *pFd,unsigned long long Size)
-   : d(NULL)
+pkgTagFile::pkgTagFile(FileFd * const pFd,unsigned long long const Size)
+   : d(new pkgTagFilePrivate(pFd, Size + 4))
 {
    Init(pFd, Size);
 }
-
-void pkgTagFile::Init(FileFd *pFd,unsigned long long Size)
+void pkgTagFile::Init(FileFd * const 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);
+   d->Reset(pFd, Size);
 
-   if (d->Fd.IsOpen() == false)
+   if (d->Fd->IsOpen() == false)
       d->Start = d->End = d->Buffer = 0;
    else
       d->Buffer = (char*)malloc(sizeof(char) * Size);
@@ -124,7 +135,6 @@ void pkgTagFile::Init(FileFd *pFd,unsigned long long Size)
 /* */
 pkgTagFile::~pkgTagFile()
 {
-   free(d->Buffer);
    delete d;
 }
                                                                        /*}}}*/
@@ -183,8 +193,8 @@ bool pkgTagFile::Step(pkgTagSection &Tag)
            break;
 
         if (Resize() == false)
-           return _error->Error(_("Unable to parse package file %s (1)"),
-                 d->Fd.Name().c_str());
+           return _error->Error(_("Unable to parse package file %s (%d)"),
+                 d->Fd->Name().c_str(), 1);
 
       } while (Tag.Scan(d->Start,d->End - d->Start, false) == false);
    }
@@ -213,7 +223,7 @@ bool pkgTagFile::Fill()
    {
       // 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)
+      if (d->Fd->Read(d->End, dataSize, &Actual) == false)
         return false;
       if (Actual != dataSize)
         d->Done = true;
@@ -268,7 +278,7 @@ bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long long Offset)
    // Reposition and reload..
    d->iOffset = Offset;
    d->Done = false;
-   if (d->Fd.Seek(Offset) == false)
+   if (d->Fd->Seek(Offset) == false)
       return false;
    d->End = d->Start = d->Buffer;
    
@@ -283,7 +293,7 @@ bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long long Offset)
       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 _error->Error(_("Unable to parse package file %s (%d)"),d->Fd->Name().c_str(), 2);
    
    return true;
 }
@@ -293,24 +303,13 @@ 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));
 }
 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)
 {
    Section = Start;
@@ -336,11 +335,7 @@ bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength, bool const R
       }
       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;
@@ -358,7 +353,7 @@ bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength, bool const R
          return true;
 
       // 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)
@@ -367,10 +362,6 @@ bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength, bool const R
               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);
         }
@@ -384,14 +375,16 @@ 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);
         // find the beginning of the value
         Stop = Colon + 1;
-        for (; isspace(*Stop) != 0; ++Stop);
+        for (; isspace_ascii(*Stop) != 0; ++Stop)
+           if (*Stop == '\n' && Stop[1] != ' ')
+              break;
         if (Stop >= End)
            return false;
         lastTagData.StartValue = Stop - Section;
@@ -414,16 +407,10 @@ bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength, bool const R
            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;
@@ -454,11 +441,7 @@ void pkgTagSection::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
 {
    unsigned int tmp;
    return Find(Tag, tmp);
@@ -503,14 +486,12 @@ bool pkgTagSection::Find(const char *Tag,const char *&Start,
    if (unlikely(Start > End))
       return _error->Error("Internal parsing error");
 
-   for (; isspace(End[-1]) != 0 && End > Start; --End);
+   for (; isspace_ascii(End[-1]) != 0 && End > Start; --End);
 
    return true;
 }
                                                                        /*}}}*/
 // TagSection::FindS - Find a string                                   /*{{{*/
-// ---------------------------------------------------------------------
-/* */
 string pkgTagSection::FindS(const char *Tag) const
 {
    const char *Start;
@@ -520,6 +501,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_ascii(End[-1]) != 0 && End > Start; --End);
+
+   return std::string(Start, End - Start);
+}
+                                                                       /*}}}*/
 // TagSection::FindI - Find an integer                                 /*{{{*/
 // ---------------------------------------------------------------------
 /* */
@@ -536,9 +535,15 @@ 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;
@@ -582,6 +587,34 @@ bool pkgTagSection::FindB(const char *Tag, bool const &Default) const
 // TagSection::FindFlag - Locate a yes/no type flag                    /*{{{*/
 // ---------------------------------------------------------------------
 /* The bits marked in Flag are masked on/off in Flags */
+bool pkgTagSection::FindFlag(const char * const Tag, uint8_t &Flags,
+                            uint8_t const Flag) const
+{
+   const char *Start;
+   const char *Stop;
+   if (Find(Tag,Start,Stop) == false)
+      return true;
+   return FindFlag(Flags, Flag, Start, Stop);
+}
+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::FindFlag(const char *Tag,unsigned long &Flags,
                             unsigned long Flag) const
 {
@@ -623,71 +656,149 @@ 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_ascii(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;
+}
+                                                                       /*}}}*/
+
+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                                        /*{{{*/
 // ---------------------------------------------------------------------
 /* 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 +828,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);
@@ -773,7 +884,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 +913,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 +922,7 @@ bool TFRewrite(FILE *Output,pkgTagSection const &Tags,const char *Order[],
       
    return true;
 }
+APT_IGNORE_DEPRECATED_POP
                                                                        /*}}}*/
 
 pkgTagSection::~pkgTagSection() { delete d; }