// -*- mode: cpp; mode: fold -*- // Description /*{{{*/ /* ###################################################################### Debian Version - Versioning system for Debian This implements the standard Debian versioning system. ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ #include <config.h> #include <apt-pkg/debversion.h> #include <apt-pkg/pkgcache.h> #include <ctype.h> #include <stdlib.h> #include <string.h> /*}}}*/ debVersioningSystem debVS; // debVS::debVersioningSystem - Constructor /*{{{*/ // --------------------------------------------------------------------- /* */ debVersioningSystem::debVersioningSystem() { Label = "Standard .deb"; } /*}}}*/ // debVS::CmpFragment - Compare versions /*{{{*/ // --------------------------------------------------------------------- /* This compares a fragment of the version. This is a slightly adapted version of what dpkg uses in dpkg/lib/dpkg/version.c. In particular, the a | b = NULL check is removed as we check this in the caller, we use an explicit end for a | b strings and we check ~ explicit. */ static int order(char c) { if (isdigit(c)) return 0; else if (isalpha(c)) return c; else if (c == '~') return -1; else if (c) return c + 256; else return 0; } int debVersioningSystem::CmpFragment(const char *A,const char *AEnd, const char *B,const char *BEnd) { /* Iterate over the whole string What this does is to split the whole string into groups of numeric and non numeric portions. For instance: a67bhgs89 Has 4 portions 'a', '67', 'bhgs', '89'. A more normal: 2.7.2-linux-1 Has '2', '.', '7', '.' ,'-linux-','1' */ const char *lhs = A; const char *rhs = B; while (lhs != AEnd && rhs != BEnd) { int first_diff = 0; while (lhs != AEnd && rhs != BEnd && (!isdigit(*lhs) || !isdigit(*rhs))) { int vc = order(*lhs); int rc = order(*rhs); if (vc != rc) return vc - rc; ++lhs; ++rhs; } while (*lhs == '0') ++lhs; while (*rhs == '0') ++rhs; while (isdigit(*lhs) && isdigit(*rhs)) { if (!first_diff) first_diff = *lhs - *rhs; ++lhs; ++rhs; } if (isdigit(*lhs)) return 1; if (isdigit(*rhs)) return -1; if (first_diff) return first_diff; } // The strings must be equal if (lhs == AEnd && rhs == BEnd) return 0; // lhs is shorter if (lhs == AEnd) { if (*rhs == '~') return 1; return -1; } // rhs is shorter if (rhs == BEnd) { if (*lhs == '~') return -1; return 1; } // Shouldn't happen return 1; } /*}}}*/ // debVS::CmpVersion - Comparison for versions /*{{{*/ // --------------------------------------------------------------------- /* This fragments the version into E:V-R triples and compares each portion separately. */ int debVersioningSystem::DoCmpVersion(const char *A,const char *AEnd, const char *B,const char *BEnd) { // Strip off the epoch and compare it const char *lhs = (const char*) memchr(A, ':', AEnd - A); const char *rhs = (const char*) memchr(B, ':', BEnd - B); if (lhs == NULL) lhs = A; if (rhs == NULL) rhs = B; // Special case: a zero epoch is the same as no epoch, // so remove it. if (lhs != A) { for (; *A == '0'; ++A); if (A == lhs) { ++A; ++lhs; } } if (rhs != B) { for (; *B == '0'; ++B); if (B == rhs) { ++B; ++rhs; } } // Compare the epoch int Res = CmpFragment(A,lhs,B,rhs); if (Res != 0) return Res; // Skip the : if (lhs != A) lhs++; if (rhs != B) rhs++; // Find the last - const char *dlhs = (const char*) memrchr(lhs, '-', AEnd - lhs); const char *drhs = (const char*) memrchr(rhs, '-', BEnd - rhs); if (dlhs == NULL) dlhs = AEnd; if (drhs == NULL) drhs = BEnd; // Compare the main version Res = CmpFragment(lhs,dlhs,rhs,drhs); if (Res != 0) return Res; // Skip the - if (dlhs != lhs) dlhs++; if (drhs != rhs) drhs++; // no debian revision need to be treated like -0 if (*(dlhs-1) == '-' && *(drhs-1) == '-') return CmpFragment(dlhs,AEnd,drhs,BEnd); else if (*(dlhs-1) == '-') { const char* null = "0"; return CmpFragment(dlhs,AEnd,null, null+1); } else if (*(drhs-1) == '-') { const char* null = "0"; return CmpFragment(null, null+1, drhs, BEnd); } else return 0; } /*}}}*/ // debVS::CheckDep - Check a single dependency /*{{{*/ // --------------------------------------------------------------------- /* This simply performs the version comparison and switch based on operator. If DepVer is 0 then we are comparing against a provides with no version. */ bool debVersioningSystem::CheckDep(const char *PkgVer, int Op,const char *DepVer) { if (DepVer == 0 || DepVer[0] == 0) return true; if (PkgVer == 0 || PkgVer[0] == 0) return false; Op &= 0x0F; // fast track for (equal) strings [by location] which are by definition equal versions if (PkgVer == DepVer) return Op == pkgCache::Dep::Equals || Op == pkgCache::Dep::LessEq || Op == pkgCache::Dep::GreaterEq; // Perform the actual comparison. int const Res = CmpVersion(PkgVer, DepVer); switch (Op) { case pkgCache::Dep::LessEq: if (Res <= 0) return true; break; case pkgCache::Dep::GreaterEq: if (Res >= 0) return true; break; case pkgCache::Dep::Less: if (Res < 0) return true; break; case pkgCache::Dep::Greater: if (Res > 0) return true; break; case pkgCache::Dep::Equals: if (Res == 0) return true; break; case pkgCache::Dep::NotEquals: if (Res != 0) return true; break; } return false; } /*}}}*/ // debVS::UpstreamVersion - Return the upstream version string /*{{{*/ // --------------------------------------------------------------------- /* This strips all the debian specific information from the version number */ std::string debVersioningSystem::UpstreamVersion(const char *Ver) { // Strip off the bit before the first colon const char *I = Ver; for (; *I != 0 && *I != ':'; I++); if (*I == ':') Ver = I + 1; // Chop off the trailing - I = Ver; unsigned Last = strlen(Ver); for (; *I != 0; I++) if (*I == '-') Last = I - Ver; return std::string(Ver,Last); } /*}}}*/