X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/c72f5c4fb4b1d496a9bbdb421f25e986f0cba9bf..cc0a4c82b3c132abba9b9ec35fd61bc8b45a1b80:/apt-pkg/tagfile.cc?ds=sidebyside diff --git a/apt-pkg/tagfile.cc b/apt-pkg/tagfile.cc index a0b64f9ca..3a3a3a04a 100644 --- a/apt-pkg/tagfile.cc +++ b/apt-pkg/tagfile.cc @@ -17,6 +17,9 @@ #include #include #include +#include + +#include #include #include @@ -28,34 +31,47 @@ /*}}}*/ using std::string; +using APT::StringView; -class pkgTagFilePrivate +class APT_HIDDEN pkgTagFilePrivate /*{{{*/ { public: - void Reset(FileFd * const pFd, unsigned long long const pSize) + void Reset(FileFd * const pFd, unsigned long long const pSize, pkgTagFile::Flags const pFlags) { if (Buffer != NULL) free(Buffer); Buffer = NULL; Fd = pFd; + Flags = pFlags; Start = NULL; End = NULL; Done = false; iOffset = 0; Size = pSize; + isCommentedLine = false; + chunks.clear(); } - pkgTagFilePrivate(FileFd * const pFd, unsigned long long const Size) : Buffer(NULL) + pkgTagFilePrivate(FileFd * const pFd, unsigned long long const Size, pkgTagFile::Flags const pFlags) : Buffer(NULL) { - Reset(pFd, Size); + Reset(pFd, Size, pFlags); } FileFd * Fd; + pkgTagFile::Flags Flags; char *Buffer; char *Start; char *End; bool Done; unsigned long long iOffset; unsigned long long Size; + bool isCommentedLine; + struct FileChunk + { + bool const good; + size_t length; + FileChunk(bool const pgood, size_t const plength) : good(pgood), length(plength) {} + }; + std::list chunks; ~pkgTagFilePrivate() { @@ -63,8 +79,8 @@ public: free(Buffer); } }; - -class pkgTagSectionPrivate + /*}}}*/ +class APT_HIDDEN pkgTagSectionPrivate /*{{{*/ { public: pkgTagSectionPrivate() @@ -80,6 +96,7 @@ public: }; std::vector Tags; }; + /*}}}*/ static unsigned long AlphaHash(const char *Text, size_t Length) /*{{{*/ { @@ -98,21 +115,23 @@ static unsigned long AlphaHash(const char *Text, size_t Length) /*{{{*/ /*}}}*/ // TagFile::pkgTagFile - Constructor /*{{{*/ -// --------------------------------------------------------------------- -/* */ +pkgTagFile::pkgTagFile(FileFd * const pFd,pkgTagFile::Flags const pFlags, unsigned long long const Size) + : d(new pkgTagFilePrivate(pFd, Size + 4, pFlags)) +{ + Init(pFd, pFlags, Size); +} pkgTagFile::pkgTagFile(FileFd * const pFd,unsigned long long const Size) - : d(new pkgTagFilePrivate(pFd, Size + 4)) + : pkgTagFile(pFd, pkgTagFile::STRICT, Size) { - Init(pFd, Size); } -void pkgTagFile::Init(FileFd * const pFd,unsigned long long Size) +void pkgTagFile::Init(FileFd * const pFd, pkgTagFile::Flags const pFlags, 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; - d->Reset(pFd, Size); + d->Reset(pFd, Size, pFlags); if (d->Fd->IsOpen() == false) d->Start = d->End = d->Buffer = 0; @@ -128,11 +147,13 @@ void pkgTagFile::Init(FileFd * const pFd,unsigned long long Size) d->iOffset = 0; if (d->Done == false) Fill(); +} +void pkgTagFile::Init(FileFd * const pFd,unsigned long long Size) +{ + Init(pFd, pkgTagFile::STRICT, Size); } /*}}}*/ // TagFile::~pkgTagFile - Destructor /*{{{*/ -// --------------------------------------------------------------------- -/* */ pkgTagFile::~pkgTagFile() { delete d; @@ -162,7 +183,7 @@ 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); + char* const newBuffer = static_cast(realloc(d->Buffer, sizeof(char) * newSize)); if (newBuffer == NULL) return false; d->Buffer = newBuffer; @@ -199,8 +220,35 @@ bool pkgTagFile::Step(pkgTagSection &Tag) } while (Tag.Scan(d->Start,d->End - d->Start, false) == false); } - d->Start += Tag.size(); - d->iOffset += Tag.size(); + size_t tagSize = Tag.size(); + d->Start += tagSize; + + if ((d->Flags & pkgTagFile::SUPPORT_COMMENTS) == 0) + d->iOffset += tagSize; + else + { + auto first = d->chunks.begin(); + for (; first != d->chunks.end(); ++first) + { + if (first->good == false) + d->iOffset += first->length; + else + { + if (tagSize < first->length) + { + first->length -= tagSize; + d->iOffset += tagSize; + break; + } + else + { + tagSize -= first->length; + d->iOffset += first->length; + } + } + } + d->chunks.erase(d->chunks.begin(), first); + } Tag.Trim(); return true; @@ -210,49 +258,166 @@ bool pkgTagFile::Step(pkgTagSection &Tag) // --------------------------------------------------------------------- /* This takes the bit at the end of the buffer and puts it at the start then fills the rest from the file */ +static bool FillBuffer(pkgTagFilePrivate * const d) +{ + unsigned long long Actual = 0; + // 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; + return true; +} +static void RemoveCommentsFromBuffer(pkgTagFilePrivate * const d) +{ + // look for valid comments in the buffer + char * good_start = nullptr, * bad_start = nullptr; + char * current = d->Start; + if (d->isCommentedLine == false) + { + if (d->Start == d->Buffer) + { + // the start of the buffer is a newline as a record can't start + // in the middle of a line by definition. + if (*d->Start == '#') + { + d->isCommentedLine = true; + ++current; + if (current > d->End) + d->chunks.emplace_back(false, 1); + } + } + if (d->isCommentedLine == false) + good_start = d->Start; + else + bad_start = d->Start; + } + else + bad_start = d->Start; + + std::vector> good_parts; + while (current <= d->End) + { + size_t const restLength = (d->End - current) + 1; + if (d->isCommentedLine == false) + { + current = static_cast(memchr(current, '#', restLength)); + if (current == nullptr) + { + size_t const goodLength = d->End - good_start; + d->chunks.emplace_back(true, goodLength); + if (good_start != d->Start) + good_parts.push_back(std::make_pair(good_start, goodLength)); + break; + } + bad_start = current; + --current; + // ensure that this is really a comment and not a '#' in the middle of a line + if (*current == '\n') + { + size_t const goodLength = (current - good_start) + 1; + d->chunks.emplace_back(true, goodLength); + good_parts.push_back(std::make_pair(good_start, goodLength)); + good_start = nullptr; + d->isCommentedLine = true; + } + current += 2; + } + else // the current line is a comment + { + current = static_cast(memchr(current, '\n', restLength)); + if (current == nullptr) + { + d->chunks.emplace_back(false, (d->End - bad_start)); + break; + } + ++current; + // is the next line a comment, too? + if (current > d->End || *current != '#') + { + d->chunks.emplace_back(false, (current - bad_start)); + good_start = current; + bad_start = nullptr; + d->isCommentedLine = false; + } + ++current; + } + } + + if (good_parts.empty() == false) + { + // we found comments, so move later parts over them + current = d->Start; + for (auto const &good: good_parts) + { + memmove(current, good.first, good.second); + current += good.second; + } + d->End = current; + } + + if (d->isCommentedLine == true) + { + // deal with a buffer containing only comments + // or an (unfinished) comment at the end + if (good_parts.empty() == true) + d->End = d->Start; + else + d->Start = d->End; + } + else + { + // the buffer was all comment, but ended with the buffer + if (good_parts.empty() == true && good_start >= d->End) + d->End = d->Start; + else + d->Start = d->End; + } +} bool pkgTagFile::Fill() { - unsigned long long EndSize = d->End - d->Start; + unsigned long long const EndSize = d->End - d->Start; + if (EndSize != 0) + { + memmove(d->Buffer,d->Start,EndSize); + d->Start = d->End = d->Buffer + EndSize; + } + else + d->Start = d->End = d->Buffer; + unsigned long long Actual = 0; - - memmove(d->Buffer,d->Start,EndSize); - d->Start = d->Buffer; - d->End = d->Buffer + EndSize; - - if (d->Done == false) + while (d->Done == false && d->Size > (Actual + 1)) { - // 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 (FillBuffer(d) == false) return false; - if (Actual != dataSize) - d->Done = true; - d->End += Actual; + if ((d->Flags & pkgTagFile::SUPPORT_COMMENTS) != 0) + RemoveCommentsFromBuffer(d); + Actual = d->End - d->Buffer; } - + d->Start = d->Buffer; + 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++; + ++LineCount; if (LineCount < 2) { - if ((unsigned)(d->End - d->Buffer) >= d->Size) + if (static_cast(d->End - d->Buffer) >= d->Size) Resize(d->Size + 3); - for (; LineCount < 2; LineCount++) + for (; LineCount < 2; ++LineCount) *d->End++ = '\n'; } - - return true; } - return true; } /*}}}*/ @@ -262,8 +427,9 @@ bool pkgTagFile::Fill() that is there */ bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long long Offset) { + if ((d->Flags & pkgTagFile::SUPPORT_COMMENTS) == 0 && // We are within a buffer space of the next hit.. - if (Offset >= d->iOffset && d->iOffset + (d->End - d->Start) > Offset) + Offset >= d->iOffset && d->iOffset + (d->End - d->Start) > Offset) { unsigned long long Dist = Offset - d->iOffset; d->Start += Dist; @@ -281,7 +447,9 @@ bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long long Offset) if (d->Fd->Seek(Offset) == false) return false; d->End = d->Start = d->Buffer; - + d->isCommentedLine = false; + d->chunks.clear(); + if (Fill() == false) return false; @@ -382,7 +550,7 @@ bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength, bool const R lastTagHash = AlphaHash(Stop, EndTag - Stop); // find the beginning of the value Stop = Colon + 1; - for (; isspace_ascii(*Stop) != 0; ++Stop) + for (; Stop < End && isspace_ascii(*Stop) != 0; ++Stop) if (*Stop == '\n' && Stop[1] != ' ') break; if (Stop >= End) @@ -441,7 +609,7 @@ void pkgTagSection::Trim() } /*}}}*/ // TagSection::Exists - return True if a tag exists /*{{{*/ -bool pkgTagSection::Exists(const char* const Tag) const +bool pkgTagSection::Exists(StringView Tag) const { unsigned int tmp; return Find(Tag, tmp); @@ -450,9 +618,10 @@ bool pkgTagSection::Exists(const char* const Tag) const // 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(StringView TagView,unsigned int &Pos) const { - size_t const Length = strlen(Tag); + const char * const Tag = TagView.data(); + size_t const Length = TagView.length(); unsigned int Bucket = AlphaIndexes[AlphaHash(Tag, Length)]; if (Bucket == 0) return false; @@ -473,7 +642,8 @@ 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::Find(StringView Tag,const char *&Start, const char *&End) const { unsigned int Pos; @@ -492,17 +662,17 @@ bool pkgTagSection::Find(const char *Tag,const char *&Start, } /*}}}*/ // 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); } /*}}}*/ // TagSection::FindRawS - Find a string /*{{{*/ -string pkgTagSection::FindRawS(const char *Tag) const +StringView pkgTagSection::FindRaw(StringView Tag) const { unsigned int Pos; if (Find(Tag, Pos) == false) @@ -516,13 +686,13 @@ string pkgTagSection::FindRawS(const char *Tag) const for (; isspace_ascii(End[-1]) != 0 && End > Start; --End); - return std::string(Start, End - Start); + return StringView(Start, End - Start); } /*}}}*/ // TagSection::FindI - Find an integer /*{{{*/ // --------------------------------------------------------------------- /* */ -signed int pkgTagSection::FindI(const char *Tag,signed long Default) const +signed int pkgTagSection::FindI(StringView Tag,signed long Default) const { const char *Start; const char *Stop; @@ -552,7 +722,7 @@ signed int pkgTagSection::FindI(const char *Tag,signed long Default) const // 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::FindULL(StringView Tag, unsigned long long const &Default) const { const char *Start; const char *Stop; @@ -576,7 +746,7 @@ unsigned long long pkgTagSection::FindULL(const char *Tag, unsigned long long co // TagSection::FindB - Find boolean value /*{{{*/ // --------------------------------------------------------------------- /* */ -bool pkgTagSection::FindB(const char *Tag, bool const &Default) const +bool pkgTagSection::FindB(StringView Tag, bool Default) const { const char *Start, *Stop; if (Find(Tag, Start, Stop) == false) @@ -587,7 +757,7 @@ 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, +bool pkgTagSection::FindFlag(StringView Tag, uint8_t &Flags, uint8_t const Flag) const { const char *Start; @@ -615,7 +785,7 @@ bool pkgTagSection::FindFlag(uint8_t &Flags, uint8_t const Flag, } return true; } -bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags, +bool pkgTagSection::FindFlag(StringView Tag,unsigned long &Flags, unsigned long Flag) const { const char *Start; @@ -644,11 +814,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; @@ -672,13 +843,13 @@ pkgTagSection::Tag pkgTagSection::Tag::Rewrite(std::string const &Name, std::str else return Tag(REWRITE, Name, Data); } -static bool WriteTag(FileFd &File, std::string Tag, std::string const &Value) +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); + Tag.append(Value.data(), Value.length()); Tag.append("\n"); return File.Write(Tag.c_str(), Tag.length()); } @@ -698,7 +869,7 @@ static bool RewriteTags(FileFd &File, pkgTagSection const * const This, char con } 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()); + data = This->FindRaw(R->Name.c_str()).to_string(); else continue; @@ -722,7 +893,7 @@ bool pkgTagSection::Write(FileFd &File, char const * const * const Order, std::v if (Exists(Order[I]) == false) continue; - if (WriteTag(File, Order[I], FindRawS(Order[I])) == false) + if (WriteTag(File, Order[I], FindRaw(Order[I])) == false) return false; } } @@ -752,7 +923,7 @@ bool pkgTagSection::Write(FileFd &File, char const * const * const Order, std::v if (R != Rewrite.end()) continue; - if (WriteTag(File, name, FindRawS(name.c_str())) == false) + if (WriteTag(File, name, FindRaw(name)) == false) return false; } } @@ -776,7 +947,7 @@ bool pkgTagSection::Write(FileFd &File, char const * const * const Order, std::v continue; } - if (WriteTag(File, name, ((R->Action == Tag::RENAME) ? FindRawS(R->Name.c_str()) : R->Data)) == false) + if (WriteTag(File, name, ((R->Action == Tag::RENAME) ? FindRaw(R->Name) : R->Data)) == false) return false; } return true; @@ -840,7 +1011,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;