repoindex/alpm/package.cpp

336 lines
12 KiB
C++

#include "package.h"
#include "alpmdatabase.h"
#include "utilities.h"
#include "repository.h"
#include <QJsonObject>
#include <QJsonValue>
#include <QJsonArray>
#include <QVariant>
using namespace std;
using namespace ChronoUtilities;
namespace PackageManagement {
/*!
* \class The Package class holds meta information about an
* Arch Linux package.
*/
/*!
* \brief Constructs a new package instance.
*
* Since it is intenced to use the Package class as base class only,
* this constructor is protected.
*/
Package::Package(const QString &name, Repository *source) :
m_origin(PackageOrigin::Unknown),
m_source(source),
m_hasGeneralInfo(false),
m_name(name),
m_hasInstallScript(false),
m_hasBuildRelatedMetaData(false),
m_hasInstallRelatedMetaData(false),
m_validationMethods(ALPM_PKG_VALIDATION_UNKNOWN),
m_installReason(ALPM_PKG_REASON_EXPLICIT),
m_id(-1),
m_categoryId(-1),
m_votes(-1)
{
// initialization must be done in derived class
}
Package::~Package()
{}
bool Package::matches(const QString &name, const QString &version, const Dependency &dependency)
{
if(name == dependency.name) {
PackageVersionComparsion cmp;
switch(dependency.mode) {
case ALPM_DEP_MOD_ANY:
return true;
case ALPM_DEP_MOD_EQ:
return version == dependency.version;
case ALPM_DEP_MOD_GE:
return (cmp = PackageVersion(version).compare(PackageVersion(dependency.version))) == PackageVersionComparsion::Equal || cmp == PackageVersionComparsion::NewerThenSyncVersion;
case ALPM_DEP_MOD_LE:
return (cmp = PackageVersion(version).compare(PackageVersion(dependency.version))) == PackageVersionComparsion::Equal || cmp == PackageVersionComparsion::PackageUpgradeOnly || cmp == PackageVersionComparsion::SoftwareUpgrade;
case ALPM_DEP_MOD_GT:
return PackageVersion(version).compare(PackageVersion(dependency.version)) == PackageVersionComparsion::NewerThenSyncVersion;
case ALPM_DEP_MOD_LT:
return (cmp = PackageVersion(version).compare(PackageVersion(dependency.version))) == PackageVersionComparsion::PackageUpgradeOnly || cmp == PackageVersionComparsion::SoftwareUpgrade;
default:
;
}
}
return false;
}
/*!
* \cond
*/
namespace Utilities {
inline void put(QJsonObject &obj, const QString &key, const QJsonValue &value)
{
if(!value.isNull()) {
obj.insert(key, value);
}
}
inline void put(QJsonObject &obj, const QString &key, const DateTime dateTime)
{
if(!dateTime.isNull()) {
put(obj, key, QString::fromLocal8Bit(dateTime.toString().data()));
}
}
inline void put(QJsonObject &obj, const QString &key, const QStringList &values)
{
if(!values.isEmpty()) {
put(obj, key, QJsonArray::fromStringList(values));
}
}
void put(QJsonObject &obj, const QString &key, const QList<Dependency> &dependencies)
{
if(!dependencies.isEmpty()) {
QJsonArray jsonArray;
for(const auto &dep : dependencies) {
QJsonObject depObj;
depObj.insert(QStringLiteral("name"), dep.name);
depObj.insert(QStringLiteral("ver"), dep.version);
switch(dep.mode) {
case ALPM_DEP_MOD_ANY:
depObj.insert(QStringLiteral("mod"), QStringLiteral("any"));
break;
case ALPM_DEP_MOD_EQ:
depObj.insert(QStringLiteral("mod"), QStringLiteral("eq"));
break;
case ALPM_DEP_MOD_GE:
depObj.insert(QStringLiteral("mod"), QStringLiteral("ge"));
break;
case ALPM_DEP_MOD_LE:
depObj.insert(QStringLiteral("mod"), QStringLiteral("le"));
break;
case ALPM_DEP_MOD_GT:
depObj.insert(QStringLiteral("mod"), QStringLiteral("gt"));
break;
case ALPM_DEP_MOD_LT:
depObj.insert(QStringLiteral("mod"), QStringLiteral("lt"));
break;
}
jsonArray << depObj;
}
put(obj, key, jsonArray);
}
}
}
/*!
* \endcond
*/
using namespace Utilities;
/*!
* \brief Returns basic information about the packages as JSON object.
*/
QJsonObject Package::basicInfo(bool includeRepoAndName) const
{
QJsonObject info;
if(includeRepoAndName) {
if(source()) {
put(info, QStringLiteral("repo"), source()->name());
}
put(info, QStringLiteral("name"), name());
}
put(info, QStringLiteral("archs"), architectures());
put(info, QStringLiteral("arch"), buildArchitecture());
put(info, QStringLiteral("ver"), version());
put(info, QStringLiteral("desc"), description());
put(info, QStringLiteral("bdate"), buildDate());
put(info, QStringLiteral("flagdate"), outOfDate());
return info;
}
/*!
* \brief Returns full information about the package as JSON object.
*/
QJsonObject Package::fullInfo(bool includeRepoAndName) const
{
QJsonObject info;
if(includeRepoAndName) {
if(source()) {
put(info, QStringLiteral("repo"), source()->name());
}
put(info, QStringLiteral("name"), name());
}
put(info, QStringLiteral("archs"), architectures());
put(info, QStringLiteral("arch"), buildArchitecture());
put(info, QStringLiteral("ver"), version());
put(info, QStringLiteral("desc"), description());
put(info, QStringLiteral("bdate"), buildDate());
put(info, QStringLiteral("bdate"), buildDate());
put(info, QStringLiteral("flagdate"), outOfDate());
put(info, QStringLiteral("idate"), installDate());
put(info, QStringLiteral("isize"), QJsonValue(static_cast<long long int>(installedSize())));
put(info, QStringLiteral("url"), upstreamUrl());
put(info, QStringLiteral("lic"), licenses());
put(info, QStringLiteral("grp"), groups());
put(info, QStringLiteral("prov"), provides());
put(info, QStringLiteral("optd"), optionalDependencies());
put(info, QStringLiteral("deps"), dependencies());
put(info, QStringLiteral("requ"), requiredBy());
put(info, QStringLiteral("optf"), optionalFor());
put(info, QStringLiteral("conf"), conflicts());
put(info, QStringLiteral("repl"), replaces());
put(info, QStringLiteral("pack"), packer());
put(info, QStringLiteral("expl"), QJsonValue(installReason() == ALPM_PKG_REASON_EXPLICIT));
put(info, QStringLiteral("scri"), QJsonValue(hasInstallScript()));
put(info, QStringLiteral("sig"), Utilities::validationMethodsStrings(validationMethods()));
put(info, QStringLiteral("file"), fileName());
put(info, QStringLiteral("files"), files());
return info;
}
/*!
* \brief The PackageVersion class helps parsing package versions.
*/
/*!
* \brief Constructs a new PackageVersion instance from the specified \a versionStr.
*/
PackageVersion::PackageVersion(const QString &versionStr)
{
// determine start offsets of version and release
const ushort *str = versionStr.utf16(), *versionBeg = nullptr, *releaseBeg = nullptr;
for(const auto *i = str; ; ++i) {
switch(*i) {
case 0:
goto terminationFound;
case ':':
if(!versionBeg && !releaseBeg) {
versionBeg = i + 1;
}
break;
case '-':
releaseBeg = i + 1;
break;
default: ;
}
}
terminationFound:
if(versionBeg) {
// epoch present
epoch = QString::fromUtf16(str, versionBeg - str - 1);
if(releaseBeg) {
// release present
version = QString::fromUtf16(versionBeg, releaseBeg - versionBeg - 1);
release = QString::fromUtf16(releaseBeg);
} else {
version = QString::fromUtf16(versionBeg);
}
} else {
// epoch not present
if(releaseBeg) {
// release present
version = QString::fromUtf16(str, releaseBeg - str - 1);
release = QString::fromUtf16(releaseBeg);
} else {
version = QString::fromUtf16(str);
}
}
}
/*!
* \brief Compares two version parts.
* \returns Returns 1 if part1 is newer then part2, -1 if part2 is newer then part1 and 0 if both parts are equal.
*/
PackageVersionPartComparsion PackageVersion::compareParts(const QString &part1, const QString &part2)
{
static const QRegExp nonAlphanumericPattern(QStringLiteral("[^a-zA-Z\\d:öäüÖÄÜß]"));
int part1Pos = 0, part2Pos = 0, part1End, part2End;
while(true) {
// determine current segments
part1End = part1.indexOf(nonAlphanumericPattern, part1Pos);
part2End = part2.indexOf(nonAlphanumericPattern, part2Pos);
auto segment1 = part1.midRef(part1Pos, part1End);
auto segment2 = part2.midRef(part2Pos, part2End);
// compare current segments
int digit1 = segment1.size();
int digit2 = segment2.size();
const auto *i1 = segment1.cbegin(), *e1 = segment1.cend();
const auto *i2 = segment2.cbegin(), *e2 = segment2.cend();
// trim leading zeros
for(; i1 < e1 && *i1 == QLatin1Char('0'); ++i1, --digit1);
for(; i2 < e2 && *i2 == QLatin1Char('0'); ++i2, --digit2);
if(digit1 > digit2) {
// segment 1 has more digits -> newer
return PackageVersionPartComparsion::Newer;
} else if (digit2 > digit1) {
// segment 2 has more digits -> newer
return PackageVersionPartComparsion::Older;
} else {
for(; i1 < e1 && i2 < e2; ++i1, ++i2) {
if(*i1 > *i2) {
// segment 1 digit has higher value -> newer
return PackageVersionPartComparsion::Newer;
} else if(*i2 > *i1) {
// segment 2 digit has higher value -> newer
return PackageVersionPartComparsion::Older;
}
}
}
// this segment is equal, look for the next segment
if(part1End >= 0 && part2End >= 0) {
// there is another segment in both parts
part1Pos = part1End + 1;
part2Pos = part2End + 1;
} else if(part1End >= 0) {
// only part 1 has another segment -> it is more specific and hence considered newer
return PackageVersionPartComparsion::Newer;
} else if(part2End >= 0) {
// only part 2 has another segment -> it is more specific and hence considered newer
return PackageVersionPartComparsion::Older;
} else {
// none of the parts has another segment -> parts are equal
return PackageVersionPartComparsion::Equal;
}
}
}
/*!
* \brief Compares this version with another version.
*
* This method distinguishes between software upgrades and package releases. See Alpm::PackageVersionComparsion enum.
*/
PackageVersionComparsion PackageVersion::compare(const PackageVersion &other) const
{
if(!epoch.isEmpty() || !other.epoch.isEmpty()) {
switch(compareParts(other.epoch, epoch)) {
case PackageVersionPartComparsion::Newer: return PackageVersionComparsion::SoftwareUpgrade;
case PackageVersionPartComparsion::Older: return PackageVersionComparsion::NewerThenSyncVersion;
case PackageVersionPartComparsion::Equal: ;
}
}
switch(compareParts(other.version, version)) {
case PackageVersionPartComparsion::Newer: return PackageVersionComparsion::SoftwareUpgrade;
case PackageVersionPartComparsion::Older: return PackageVersionComparsion::NewerThenSyncVersion;
case PackageVersionPartComparsion::Equal: ;
}
switch(compareParts(other.release, release)) {
case PackageVersionPartComparsion::Newer: return PackageVersionComparsion::PackageUpgradeOnly;
case PackageVersionPartComparsion::Older: return PackageVersionComparsion::NewerThenSyncVersion;
case PackageVersionPartComparsion::Equal: ;
}
return PackageVersionComparsion::Equal;
}
}