diff options
author | Fabian Groffen <grobian@gentoo.org> | 2022-04-18 11:35:48 +0200 |
---|---|---|
committer | Fabian Groffen <grobian@gentoo.org> | 2022-04-18 11:35:48 +0200 |
commit | 7de2f154c775e9de276f2fc1f619922f48a13b90 (patch) | |
tree | 654a5408018bc8fc18c32b5e7c1d664c17549fb8 /libq | |
parent | qcheck: fix config-protect check, bug #837188 (diff) | |
download | portage-utils-7de2f154c775e9de276f2fc1f619922f48a13b90.tar.gz portage-utils-7de2f154c775e9de276f2fc1f619922f48a13b90.tar.bz2 portage-utils-7de2f154c775e9de276f2fc1f619922f48a13b90.zip |
libq/atom: implement strict PMS 3.3 in atom_compare
Version comparisons are complex, stick with strict PMS definition for
it, so we produce the same results as Portage.
Bug: https://bugs.gentoo.org/838856
Signed-off-by: Fabian Groffen <grobian@gentoo.org>
Diffstat (limited to 'libq')
-rw-r--r-- | libq/atom.c | 249 |
1 files changed, 215 insertions, 34 deletions
diff --git a/libq/atom.c b/libq/atom.c index 0b5fcdd..d1eb9a4 100644 --- a/libq/atom.c +++ b/libq/atom.c @@ -1,5 +1,5 @@ /* - * Copyright 2005-2021 Gentoo Foundation + * Copyright 2005-2022 Gentoo Foundation * Distributed under the terms of the GNU General Public License v2 * * Copyright 2005-2008 Ned Ludd - <solar@gentoo.org> @@ -694,41 +694,170 @@ atom_compare_flg(const depend_atom *data, const depend_atom *query, int flags) /* check version */ if (data->PV && query->PV) { - char *s1, *s2; - uint64_t n1, n2; - /* first we compare the version [1.0]z_alpha1 */ - s1 = data->PV; - s2 = query->PV; - while (s1 || s2) { - if (s1 && s2) { - /* deal with leading zeros */ - while (*s1 == '0' && *s2 == '0') { - ++s1; - ++s2; + char *s1; + char *ends1; + char *s2; + char *ends2; + long long n1; + long long n2; + const atom_suffix *as1; + const atom_suffix *as2; + + /* PMS 3.3 Version Comparison + * + * Algorithm 3.1: Version comparison top-level logic + * 1: let A and B be the versions to be compared + * 2: compare numeric components using Algorithm 3.2 + * 3: compare letter components using Algorithm 3.4 + * 4: compare suffixes using Algorithm 3.5 + * 5: compare revision components using Algorithm 3.7 + * 6: return A = B + */ + + /* step 2: numeric components + * + * Algorithm 3.2: Version comparison logic for numeric + * components + * 1: define the notations Ank and Bnk to mean the kth numeric + * component of A and B respectively, using 0-based indexing + * 2: if An0 > Bn0 using integer comparison then + * 3: return A > B + * 4: else if An0 < Bn0 using integer comparison then + * 5: return A < B + * 6: end if + * 7: let Ann be the number of numeric components of A + * 8: let Bnn be the number of numeric components of B + * 9: for all i such that i ≥ 1 and i < Ann and i < Bnn, in + * ascending order do + * 10: compare Ani and Bni using Algorithm 3.3 + * 11: end for + * 12: if Ann > Bnn then + * 13: return A > B + * 14: else if Ann < Bnn then + * 15: return A < B + * 16: end if + * + * Algorithm 3.3: Version comparison logic for each numeric + * component after the first + * 1: if either Ani or Bni has a leading 0 then + * 2: let An′i be Ani with any trailing 0s removed + * 3: let Bn′i be Bni with any trailing 0s removed + * 4: if An′i > Bn′i using ASCII stringwise comparison then + * 5: return A > B + * 6: else if An′i < Bn′i using ASCII stringwise comparison then + * 7: return A < B + * 8: end if + * 9: else + * 10: if Ani > Bni using integer comparison then + * 11: return A > B + * 12: else if Ani < Bni using integer comparison then + * 13: return A < B + * 14: end if + * 15: end if + */ + s1 = data->PV; /* A */ + s2 = query->PV; /* B */ + ends1 = NULL; + ends2 = NULL; + n1 = 0; + n2 = 0; + while (s1 != NULL || s2 != NULL) { + if (s1 != NULL && s2 != NULL) { + if (ends1 == NULL) { + /* 3.2#L2-6: first component integer comparison */ + n1 = strtoll(s1, &ends1, 10); + if (ends1 == s1) + n1 = -1; + n2 = strtoll(s2, &ends2, 10); + if (ends2 == s2) + n2 = -1; + } else { + /* 3.2#L9-11: run algorithm 3.3 for remaining + * components */ + + /* 3.3#L1-9: if a leading zero is present do strcmp */ + if (*s1 == '0' || *s2 == '0') { /* 3.3#L1 */ + /* find end of component */ + for (ends1 = s1; + *ends1 != '\0' && + *ends1 != '.' && + *ends1 != '_'; + ends1++) + ; + for (ends2 = s2; + *ends2 != '\0' && + *ends2 != '.' && + *ends2 != '_'; + ends2++) + ; + /* 3.3L2-3: remove *trailing* zeros */ + for (ends1--; ends1 > s1 && *ends1 == '0'; ends1--) + ; + for (ends2--; ends2 > s2 && *ends2 == '0'; ends2--) + ; + /* 3.3L4 ASCII stringwise comparison */ + n1 = ends1 - s1; + n2 = ends2 - s2; + n1 = strncmp(s1, s2, n1 > n2 ? n1 : n2); + n2 = 0; + } else { /* 3.3#L9 */ + n1 = strtoll(s1, &ends1, 10); + if (ends1 == s1) + n1 = -1; + n2 = strtoll(s2, &ends2, 10); + if (ends2 == s2) + n2 = -1; + } } - if (*s1 == '0' && isdigit(*s2)) - return _atom_compare_match(OLDER, pfx_op); - else if (*s2 == '0' && isdigit(*s1)) - return _atom_compare_match(NEWER, pfx_op); - } else if (sfx_op == ATOM_OP_STAR && !s2 && !ver_bits) + } else if (sfx_op == ATOM_OP_STAR && s2 == NULL && !ver_bits) { return _atom_compare_match(EQUAL, pfx_op); - n1 = (s1 ? atoll(s1) : 0); - n2 = (s2 ? atoll(s2) : 0); + } else { /* 3.2#L12-16 */ + if (s1 == NULL) { + n1 = -1; + n2 = 0; + ends1 = NULL; + } + else if (s2 == NULL) { + n1 = 0; + n2 = -1; + ends2 = NULL; + } + } + if (n1 < n2) return _atom_compare_match(OLDER, pfx_op); else if (n1 > n2) return _atom_compare_match(NEWER, pfx_op); - if (s1) { - s1 = strchr(s1, '.'); - if (s1) ++s1; + + s1 = *ends1 == '\0' ? NULL : ends1; + if (s1 != NULL) { + if (*s1 != '.') + s1 = strchr(s1, '.'); + if (s1 != NULL) + s1++; } - if (s2) { - s2 = strchr(s2, '.'); - if (s2) ++s2; + s2 = *ends2 == '\0' ? NULL : ends2; + if (s2 != NULL) { + if (*s2 != '.') + s2 = strchr(s2, '.'); + if (s2 != NULL) + s2++; } } - /* compare trailing letter 1.0[z]_alpha1 */ + /* step 3: compare trailing letter 1.0[z]_alpha1 + * + * Algorithm 3.4: Version comparison logic for letter components + * 1: let Al be the letter component of A if any, otherwise the + * empty string + * 2: let Bl be the letter component of B if any, otherwise the + * empty string + * 3: if Al > Bl using ASCII stringwise comparison then + * 4: return A > B + * 5: else if Al < Bl using ASCII stringwise comparison then + * 6: return A < B + * 7: end if + */ if (sfx_op == ATOM_OP_STAR) { ver_bits >>= 1; if (!query->letter && !ver_bits) @@ -738,12 +867,52 @@ atom_compare_flg(const depend_atom *data, const depend_atom *query, int flags) return _atom_compare_match(OLDER, pfx_op); if (data->letter > query->letter) return _atom_compare_match(NEWER, pfx_op); - /* find differing suffixes 1.0z[_alpha1] */ - const atom_suffix *as1 = &data->suffixes[0]; - const atom_suffix *as2 = &query->suffixes[0]; + + /* Algorithm 3.5: Version comparison logic for suffixes + * 1: define the notations Ask and Bsk to mean the kth suffix + * of A and B respectively, using 0-based indexing + * 2: let Asn be the number of suffixes of A + * 3: let Bsn be the number of suffixes of B + * 4: for all i such that i ≥ 0 and i < Asn and i < Bsn, in + * ascending order do + * 5: compare Asi and Bsi using algorithm 3.6 + * 6: end for + * 7: if Asn > Bsn then + * 8: if AsBsn is of type _p then + * 9: return A > B + * 10: else + * 11: return A < B + * 12: end if + * 13: else if Asn < Bsn then + * 14: if BsAsn is of type _p then + * 15: return A < B + * 16: else + * 17: return A > B + * 18: end if + * 19: end if + * + * Algorithm 3.6: Version comparison logic for each suffix + * 1: if Asi and Bsi are of the same type (_alpha vs _beta etc) then + * 2: let As′i be the integer part of Asi if any, otherwise 0 + * 3: let Bs′i be the integer part of Bsi if any, otherwise 0 + * 4: if As′i > Bs′i, using integer comparison then + * 5: return A > B + * 6: else if As′i < Bs′i, using integer comparison then + * 7: return A < B + * 8: end if + * 9: else if the type of Asi is greater than the type of Bsi + * using the ordering _alpha < _beta < _pre < _rc < _p then + * 10: return A > B + * 11: else + * 12: return A < B + * 13: end if + */ + + /* step 4: find differing suffixes 1.0z[_alpha1] */ + as1 = &data->suffixes[0]; + as2 = &query->suffixes[0]; while (as1->suffix == as2->suffix) { - if (as1->suffix == VER_NORM || - as2->suffix == VER_NORM) + if (as1->suffix == VER_NORM || as2->suffix == VER_NORM) break; if (as1->sint != as2->sint) @@ -752,17 +921,18 @@ atom_compare_flg(const depend_atom *data, const depend_atom *query, int flags) as1++; as2++; } + /* compare suffixes 1.0z[_alpha]1 */ if (sfx_op == ATOM_OP_STAR) { ver_bits >>= 1; if (as2->suffix == VER_NORM && !ver_bits) return _atom_compare_match(EQUAL, pfx_op); } - if (as1->suffix < as2->suffix) + if (as1->suffix < as2->suffix) /* 3.6#L9 */ return _atom_compare_match(OLDER, pfx_op); else if (as1->suffix > as2->suffix) return _atom_compare_match(NEWER, pfx_op); - /* compare suffix number 1.0z_alpha[1] */ + /* compare suffix number 1.0z_alpha[1] 3.6#L4 */ if (sfx_op == ATOM_OP_STAR && !as2->sint && !ver_bits) return _atom_compare_match(EQUAL, pfx_op); else if (as1->sint < as2->sint) @@ -773,7 +943,18 @@ atom_compare_flg(const depend_atom *data, const depend_atom *query, int flags) } else if (data->PV || query->PV) return EQUAL; - /* Make sure the -r# is the same. */ + /* Algorithm 3.7: Version comparison logic for revision components + * 1: let Ar be the integer part of the revision component of A if + * any, otherwise 0 + * 2: let Br be the integer part of the revision component of B if + * any, otherwise 0 + * 3: if Ar > Br using integer comparison then + * 4: return A > B + * 5: else if Ar < Br using integer comparison then + * 6: return A < B + * 7: end if + */ + /* Make sure the -r# is the same. 3.7 */ if ((sfx_op == ATOM_OP_STAR && query->PR_int == 0) || pfx_op == ATOM_OP_PV_EQUAL || flags & ATOM_COMP_NOREV || |