X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/4dc77823d360158d6870a5710cc8c17064f1308f..e02c3a9ec2b2f7a8d4aecd70f2ecdae27c207aa9:/apt-pkg/tagfile.cc?ds=sidebyside diff --git a/apt-pkg/tagfile.cc b/apt-pkg/tagfile.cc index 213a413cb..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; @@ -353,7 +521,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) @@ -375,14 +543,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 (; Stop < End && isspace_ascii(*Stop) != 0; ++Stop) + if (*Stop == '\n' && Stop[1] != ' ') + break; if (Stop >= End) return false; lastTagData.StartValue = Stop - Section; @@ -439,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); @@ -448,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; @@ -471,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; @@ -484,23 +656,23 @@ 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 +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) @@ -512,15 +684,15 @@ string pkgTagSection::FindRawS(const char *Tag) const if (unlikely(Start > End)) return ""; - for (; isspace(End[-1]) != 0 && End > Start; --End); + 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; @@ -533,9 +705,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::min() || Result > std::numeric_limits::max()) { + errno = ERANGE; + _error->Error(_("Cannot convert %s to integer: out of range"), S); + } if (S == End) return Default; return Result; @@ -544,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; @@ -568,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) @@ -579,7 +757,35 @@ 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 *Tag,unsigned long &Flags, +bool pkgTagSection::FindFlag(StringView 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(StringView Tag,unsigned long &Flags, unsigned long Flag) const { const char *Start; @@ -608,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; @@ -636,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(Value[0]) != 0) + 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()); } @@ -662,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; @@ -686,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; } } @@ -716,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; } } @@ -740,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; @@ -792,7 +999,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); @@ -804,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; @@ -848,7 +1055,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); @@ -877,7 +1084,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);