diff options
-rw-r--r-- | apt-pkg/acquire-item.cc | 67 | ||||
-rwxr-xr-x | test/integration/test-apt-get-changelog | 7 |
2 files changed, 63 insertions, 11 deletions
diff --git a/apt-pkg/acquire-item.cc b/apt-pkg/acquire-item.cc index 384101de3..2014a50d5 100644 --- a/apt-pkg/acquire-item.cc +++ b/apt-pkg/acquire-item.cc @@ -3710,17 +3710,62 @@ std::string pkgAcqChangelog::URI(pkgCache::VerIterator const &Ver) /*{{{*/ pkgCache::PkgIterator const Pkg = Ver.ParentPkg(); if (Pkg->CurrentVer != 0 && Pkg.CurrentVer() == Ver) { - std::string const root = _config->FindDir("Dir"); - std::string const basename = root + std::string("usr/share/doc/") + Pkg.Name() + "/changelog"; - std::string const debianname = basename + ".Debian"; - if (FileExists(debianname)) - return "copy://" + debianname; - else if (FileExists(debianname + ".gz")) - return "store://" + debianname + ".gz"; - else if (FileExists(basename)) - return "copy://" + basename; - else if (FileExists(basename + ".gz")) - return "store://" + basename + ".gz"; + auto const LocalFile = [](pkgCache::PkgIterator const &Pkg) -> std::string { + std::string const root = _config->FindDir("Dir"); + std::string const basename = root + std::string("usr/share/doc/") + Pkg.Name() + "/changelog"; + std::string const debianname = basename + ".Debian"; + auto const exts = APT::Configuration::getCompressorExtensions(); // likely we encounter only .gz + for (auto file : { debianname, basename }) + { + if (FileExists(file)) + return "copy://" + file; + for (auto const& ext : exts) + { + auto const compressedfile = file + ext; + if (FileExists(compressedfile)) + return "store://" + compressedfile; + } + } + return ""; + }(Pkg); + if (not LocalFile.empty()) + { + _error->PushToStack(); + FileFd trimmed; + if (APT::String::Startswith(LocalFile, "copy://")) + trimmed.Open(LocalFile.substr(7), FileFd::ReadOnly, FileFd::None); + else + trimmed.Open(LocalFile.substr(8), FileFd::ReadOnly, FileFd::Extension); + + bool trimmedFile = false; + if (trimmed.IsOpen()) + { + /* We want to look at the last lineā¦ in a (likely) compressed file, + which means we more or less have to uncompress the entire file. + So we skip ahead the filesize minus our choosen line size in + the hope that changelogs don't grow by being compressed to + avoid doing this costly dance on at least a bit of the file. */ + char buffer[150]; + if (auto const filesize = trimmed.FileSize(); filesize > sizeof(buffer)) + trimmed.Skip(filesize - sizeof(buffer)); + std::string_view giveaways[] = { + "# To read the complete changelog use", // Debian + "# For older changelog entries, run", // Ubuntu + }; + while (trimmed.ReadLine(buffer, sizeof(buffer)) != nullptr) + { + std::string_view const line{buffer}; + if (std::any_of(std::begin(giveaways), std::end(giveaways), [=](auto const gw) { return line.compare(0, gw.size(), gw) == 0; })) + { + trimmedFile = true; + break; + } + } + } + _error->RevertToStack(); + if (not trimmedFile) + return LocalFile; + } } } diff --git a/test/integration/test-apt-get-changelog b/test/integration/test-apt-get-changelog index b216f6f9a..c0eecba8b 100755 --- a/test/integration/test-apt-get-changelog +++ b/test/integration/test-apt-get-changelog @@ -127,6 +127,13 @@ testsuccessequal "'http://localhost:${APTHTTPPORT}/pool/main/a/awesome/awesome_4 testsuccessequal "'http://localhost:${APTHTTPPORT}/pool/main/a/awesome/awesome_42/changelog' awesome.changelog" apt changelog awesome --print-uris -o Acquire::Changelogs::AlwaysOnline=false -o Acquire::Changelogs::AlwaysOnline::Origin::Ubuntu=true testsuccessequal "'copy://${TMPWORKINGDIRECTORY}/rootdir/usr/share/doc/awesome/changelog' awesome.changelog" apt changelog awesome --print-uris -o Acquire::Changelogs::AlwaysOnline=false -o Acquire::Changelogs::AlwaysOnline::Origin::Debian=true +printf '\n# Older entries have been removed from this changelog.' >> rootdir/usr/share/doc/awesome/changelog +testsuccessequal "'copy://${TMPWORKINGDIRECTORY}/rootdir/usr/share/doc/awesome/changelog' awesome.changelog" apt changelog awesome --print-uris -o Acquire::Changelogs::AlwaysOnline=false +printf '\n# To read the complete changelog use `apt changelog awesome`.' >> rootdir/usr/share/doc/awesome/changelog +testsuccessequal "'http://localhost:${APTHTTPPORT}/pool/main/a/awesome/awesome_42/changelog' awesome.changelog" apt changelog awesome --print-uris -o Acquire::Changelogs::AlwaysOnline=false +printf '\n# No guarantees the trigger is the last line' >> rootdir/usr/share/doc/awesome/changelog +testsuccessequal "'http://localhost:${APTHTTPPORT}/pool/main/a/awesome/awesome_42/changelog' awesome.changelog" apt changelog awesome --print-uris -o Acquire::Changelogs::AlwaysOnline=false + testsuccess apt changelog awesome -d testfilestats 'awesome.changelog' '%U:%G:%a' '=' "${TEST_DEFAULT_USER}:${TEST_DEFAULT_GROUP}:644" head -n 3 awesome.changelog > awesome.change |