commit:     9fefd6f786ebb4d546a43e1b600659361efd3cb3
Author:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
AuthorDate: Sun Jun  9 08:39:03 2019 +0000
Commit:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
CommitDate: Sun Jun  9 08:39:03 2019 +0000
URL:        https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=9fefd6f7

libq/atom: add the antislot

Introduce antislot matching.  This allows to match a SLOT, but not the
given one.  This is mostly useful for finding packages to rebuild after
upgrading a dependant package.

Bug: https://bugs.gentoo.org/683430
Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org>

 libq/atom.c                           | 338 +++++++++++++++++++++-------------
 libq/atom.h                           |   3 +-
 man/include/qatom-01-antislot.include |  37 ++++
 man/include/qdepends.optdesc.yaml     |   6 +-
 man/qatom.1                           |  38 +++-
 man/qdepends.1                        |   8 +-
 qatom.c                               |  30 ++-
 qdepends.c                            |   8 +-
 tests/qatom/dotest                    |   8 +-
 9 files changed, 330 insertions(+), 146 deletions(-)

diff --git a/libq/atom.c b/libq/atom.c
index 8b1b47a..12a393a 100644
--- a/libq/atom.c
+++ b/libq/atom.c
@@ -30,7 +30,7 @@ const char * const atom_usecond_str[] = {
 };
 
 const char * const atom_blocker_str[] = {
-       "", "!", "!!"
+       "", "!", "!!", "^"
 };
 
 const char * const atom_op_str[] = {
@@ -65,9 +65,12 @@ atom_explode(const char *atom)
        if (*atom == '!') {
                ret->blocker++;
                atom++;
-       }
-       if (*atom == '!') {
-               ret->blocker++;
+               if (*atom == '!') {
+                       ret->blocker++;
+                       atom++;
+               }
+       } else if (*atom == '^') {
+               ret->blocker = ATOM_BL_ANTISLOT;
                atom++;
        }
 
@@ -101,65 +104,14 @@ atom_explode(const char *atom)
        if ((ptr = strstr(ret->CATEGORY, "::")) != NULL) {
                ret->REPO = ptr + 2;
                *ptr = '\0';
-       }
-
-       /* chip off the trailing [:SLOT] as needed */
-       if ((ptr = strrchr(ret->CATEGORY, ':')) != NULL) {
-               *ptr++ = '\0';
-               ret->SLOT = ptr;
-
-               /* deal with slot operators */
-               if ((ptr = strrchr(ret->SLOT, '=')) != NULL && ptr[1] == '\0') {
-                       ret->slotdep = ATOM_SD_ANY_REBUILD;
-                       *ptr = '\0';
-               }
-               if ((ptr = strrchr(ret->SLOT, '*')) != NULL && ptr[1] == '\0') {
-                       ret->slotdep = ATOM_SD_ANY_IGNORE;
-                       *ptr = '\0';
-               }
-
-               /* cut in two when sub-slot */
-               if ((ptr = strchr(ret->SLOT, '/')) != NULL) {
-                       *ptr++ = '\0';
-                       ret->SUBSLOT = ptr;
-               }
-       }
-
-       /* see if we have any suffix operators */
-       if ((ptr = strrchr(ret->CATEGORY, '*')) != NULL && ptr[1] == '\0') {
-               ret->sfx_op = ATOM_OP_STAR;
-               *ptr = '\0';
-       }
-
-       /* break up the CATEGORY and PVR */
-       if ((ptr = strrchr(ret->CATEGORY, '/')) != NULL) {
-               ret->PN = ptr + 1;
-               *ptr = '\0';
-
-               /* set PN to NULL if there's nothing */
-               if (ret->PN[0] == '\0')
-                       ret->PN = NULL;
-
-               /* eat extra crap in case it exists, this is a feature to allow
-                * /path/to/pkg.ebuild, doesn't work with prefix operators
-                * though */
-               if ((ptr = strrchr(ret->CATEGORY, '/')) != NULL)
-                       ret->CATEGORY = ptr + 1;
-       } else {
-               ret->PN = ret->CATEGORY;
-               ret->CATEGORY = NULL;
-       }
-
-       if (ret->PN == NULL) {
-               /* atom has no name, this is it */
-               ret->P = NULL;
-               ret->PVR = NULL;
-               return ret;
+               /* set to NULL if there's nothing */
+               if (ret->REPO[0] == '\0')
+                       ret->REPO = NULL;
        }
 
        /* hunt down build with USE dependencies */
-       if ((ptr = strrchr(ret->PN, ']')) != NULL && ptr[1] == '\0' &&
-                       (ptr = strrchr(ret->PN, '[')) != NULL)
+       if ((ptr = strrchr(ret->CATEGORY, ']')) != NULL && ptr[1] == '\0' &&
+                       (ptr = strrchr(ret->CATEGORY, '[')) != NULL)
        {
                atom_usedep *w = NULL;
                do {
@@ -219,6 +171,65 @@ atom_explode(const char *atom)
                *ptr++ = '\0';
        }
 
+       /* chip off the trailing [:SLOT] as needed */
+       if ((ptr = strrchr(ret->CATEGORY, ':')) != NULL) {
+               *ptr++ = '\0';
+               ret->SLOT = ptr;
+
+               /* deal with slot operators */
+               if ((ptr = strrchr(ret->SLOT, '=')) != NULL && ptr[1] == '\0') {
+                       ret->slotdep = ATOM_SD_ANY_REBUILD;
+                       *ptr = '\0';
+               }
+               if ((ptr = strrchr(ret->SLOT, '*')) != NULL && ptr[1] == '\0') {
+                       ret->slotdep = ATOM_SD_ANY_IGNORE;
+                       *ptr = '\0';
+               }
+
+               /* cut in two when sub-slot */
+               if ((ptr = strchr(ret->SLOT, '/')) != NULL) {
+                       *ptr++ = '\0';
+                       if (*ptr != '\0')
+                               ret->SUBSLOT = ptr;
+               }
+
+               /* set to NULL if there's nothing */
+               if (ret->SLOT[0] == '\0')
+                       ret->SLOT = NULL;
+       }
+
+       /* see if we have any suffix operators */
+       if ((ptr = strrchr(ret->CATEGORY, '*')) != NULL && ptr[1] == '\0') {
+               ret->sfx_op = ATOM_OP_STAR;
+               *ptr = '\0';
+       }
+
+       /* break up the CATEGORY and PVR */
+       if ((ptr = strrchr(ret->CATEGORY, '/')) != NULL) {
+               ret->PN = ptr + 1;
+               *ptr = '\0';
+
+               /* set PN to NULL if there's nothing */
+               if (ret->PN[0] == '\0')
+                       ret->PN = NULL;
+
+               /* eat extra crap in case it exists, this is a feature to allow
+                * /path/to/pkg.ebuild, doesn't work with prefix operators
+                * though */
+               if ((ptr = strrchr(ret->CATEGORY, '/')) != NULL)
+                       ret->CATEGORY = ptr + 1;
+       } else {
+               ret->PN = ret->CATEGORY;
+               ret->CATEGORY = NULL;
+       }
+
+       if (ret->PN == NULL) {
+               /* atom has no name, this is it */
+               ret->P = NULL;
+               ret->PVR = NULL;
+               return ret;
+       }
+
        /* CATEGORY should be all set here, PN contains everything up to
         * SLOT, REPO or '*'
         * PN must not end in a hyphen followed by anything matching version
@@ -360,30 +371,93 @@ _atom_compare_match(int ret, atom_operator op)
  * foo-1 <NOT_EQUAL> bar-1
  */
 int
-atom_compare(const depend_atom *a1, const depend_atom *a2)
+atom_compare(const depend_atom *data, const depend_atom *query)
 {
-       /* sanity check that at most one has operators */
-       if (a1->pfx_op != ATOM_OP_NONE ||
-                       a1->sfx_op != ATOM_OP_NONE ||
-                       a1->blocker != ATOM_BL_NONE)
-       {
-               /* is the other also having operators, then punt it */
-               if (a2->pfx_op != ATOM_OP_NONE ||
-                               a2->sfx_op != ATOM_OP_NONE ||
-                               a2->blocker != ATOM_BL_NONE)
+       atom_operator pfx_op;
+       atom_operator sfx_op;
+       atom_blocker bl_op;
+       unsigned int ver_bits;
+
+       /* remember:
+        * query should have operators, if data has them, they are ignored */
+
+       /* here comes the antislot: bug #683430
+        * it basically is a feature to select versions that are *not*
+        * what's queried for, but requiring SLOT to be set
+        *
+        * recap of slot operators:
+        *
+        * DEPEND  perl:=        (any slot change, rebuild)
+        *         perl:0=       (any sub-slot change, rebuild)
+        *         perl:*        (any slot will do, never rebuild)
+        *         perl:0*       (any sub-slot will do, never rebuild ?valid?)
+        *         perl:0        (effectively we can treat * as absent)
+        *
+        * VDB     perl:0/5.28=  (the slot/subslot it satisfied when merging)
+        *
+        * ebuild  perl:0/5.26
+        *         perl:0        (SLOT defaults to 0)
+        *
+        * query   perl:0        (matches perl:0, perl:0/5.28)
+        *         perl:0/5.28   (matches any perl:0/5.28)
+        *        !perl:0/5.28   (matches perl, perl:0, perl:0/5.26, perl:1)
+        *        ^perl:0/5.28   (matches perl:0/5.26, perl:0/5.30)
+        *        ^perl:0        (matches perl:1)
+        *         perl:=        (= in this case is meaningless: perl, perl:0 
...)
+        *  (with ^ being a portage-utils addition to match antislot)
+        */
+       bl_op = query->blocker;
+       if (bl_op == ATOM_BL_ANTISLOT) {
+               /* ^perl -> match anything with a SLOT */
+               if (query->SLOT == NULL && data->SLOT == NULL)
                        return NOT_EQUAL;
-
-               /* swap a1 & a2 so that a2 is the atom with operators */
-               const depend_atom *as = a2;
-               a2 = a1;
-               a1 = as;
+               if (query->SLOT != NULL) {
+                       if (query->SUBSLOT == NULL) {
+                               /* ^perl:0 -> match different SLOT */
+                               if (data->SLOT == NULL ||
+                                               strcmp(query->SLOT, data->SLOT) 
== 0)
+                                       return NOT_EQUAL;
+                       } else {
+                               /* ^perl:0/5.28 -> match SLOT, but different 
SUBSLOT */
+                               if (data->SLOT == NULL ||
+                                               strcmp(query->SLOT, data->SLOT) 
!= 0)
+                                       return NOT_EQUAL;
+                               if (data->SUBSLOT == NULL ||
+                                               strcmp(query->SUBSLOT, 
data->SUBSLOT) == 0)
+                                       return NOT_EQUAL;
+                       }
+               }
+               bl_op = ATOM_BL_NONE;  /* ease work below */
+       } else if (query->SLOT != NULL) {
+               /* check SLOT only when query side has it */
+               if (data->SLOT == NULL) {
+                       if (bl_op == ATOM_BL_NONE)
+                               return NOT_EQUAL;
+               } else {
+                       if (strcmp(query->SLOT, data->SLOT) != 0) {
+                               /* slot has differs */
+                               if (bl_op == ATOM_BL_NONE)
+                                       return NOT_EQUAL;
+                       } else {
+                               if (query->SUBSLOT != NULL) {
+                                       if (data->SUBSLOT == NULL) {
+                                               if (bl_op == ATOM_BL_NONE)
+                                                       return NOT_EQUAL;
+                                       } else {
+                                               if (strcmp(query->SUBSLOT, 
data->SUBSLOT) != 0) {
+                                                       if (bl_op == 
ATOM_BL_NONE)
+                                                               return 
NOT_EQUAL;
+                                               }
+                                       }
+                               }
+                       }
+               }
        }
 
-       atom_operator pfx_op = a2->pfx_op;
-       atom_operator sfx_op = a2->sfx_op;
-
        /* handle the inversing effect of blockers */
-       if (a2->blocker != ATOM_BL_NONE) {
+       pfx_op = query->pfx_op;
+       sfx_op = query->sfx_op;
+       if (bl_op != ATOM_BL_NONE) {
                switch (pfx_op) {
                        case ATOM_OP_NEWER:
                                pfx_op = ATOM_OP_OLDER_EQUAL;
@@ -405,60 +479,62 @@ atom_compare(const depend_atom *a1, const depend_atom *a2)
                }
        }
 
-       /* check slot only when both sides have it */
-       if (a1->SLOT != NULL && a2->SLOT != NULL &&
-                       a1->SLOT[0] != '\0' && a2->SLOT[0] != '\0' &&
-                       (strcmp(a1->SLOT, a2->SLOT) != 0 ||
-                       (a1->SUBSLOT != NULL && a2->SUBSLOT != NULL &&
-                        strcmp(a1->SUBSLOT, a2->SUBSLOT) != 0)))
-               return NOT_EQUAL;
-
-       /* same for check repo */
-       if (a1->REPO != NULL && a2->REPO != NULL &&
-                       a1->REPO[0] != '\0' && a2->REPO[0] != '\0' &&
-                       strcmp(a1->REPO, a2->REPO) != 0)
-               return NOT_EQUAL;
+       /* check REPO, if query has it, ignore blocker stuff for this one */
+       if (query->REPO != NULL) {
+               if (data->REPO == NULL)
+                       return NOT_EQUAL;
+               if (strcmp(query->REPO, data->REPO) != 0)
+                       return NOT_EQUAL;
+       }
 
-       /* Check category, iff both are specified.  This way we can match
-        * atoms like "sys-devel/gcc" and "gcc". */
-       if (a1->CATEGORY && a2->CATEGORY) {
-               if (strcmp(a1->CATEGORY, a2->CATEGORY))
+       /* check CATEGORY, if query has it, so we match
+        * atoms like "sys-devel/gcc" and "gcc" */
+       if (query->CATEGORY != NULL) {
+               if (data->CATEGORY == NULL)
                        return NOT_EQUAL;
+               if (strcmp(query->CATEGORY, data->CATEGORY) != 0) {
+                       if (bl_op == ATOM_BL_NONE)
+                               return NOT_EQUAL;
+               } else {
+                       if (bl_op != ATOM_BL_NONE && query->PN == NULL)
+                               return EQUAL;
+               }
        }
 
-       /* check name */
-       if (a1->PN && a2->PN) {
-               if (strcmp(a1->PN, a2->PN))
+       /* check PN, this may be absent if query is for CATEGORY only */
+       if (query->PN != NULL) {
+               if (data->PN == NULL)
                        return NOT_EQUAL;
-       } else if (a1->CATEGORY && a2->CATEGORY) {
-               /* if CAT is set, and one side has empty PN, accept as match */
-               return a2->blocker != ATOM_BL_NONE ? NOT_EQUAL : EQUAL;
-       } else if (a1->PN || a2->PN)
-               return NOT_EQUAL;
+               if (strcmp(query->PN, data->PN) != 0) {
+                       if (bl_op == ATOM_BL_NONE)
+                               return NOT_EQUAL;
+               } else {
+                       if (bl_op != ATOM_BL_NONE && query->PV == NULL)
+                               return EQUAL;
+               }
+       }
 
        /* in order to handle suffix globs, we need to know all of the
-        * version elements provided in it ahead of time
-        */
-       unsigned int ver_bits = 0;
+        * version elements provided in it ahead of time */
+       ver_bits = 0;
        if (sfx_op == ATOM_OP_STAR) {
-               if (a2->letter)
+               if (query->letter)
                        ver_bits |= (1 << 0);
-               if (a2->suffixes[0].suffix != VER_NORM)
+               if (query->suffixes[0].suffix != VER_NORM)
                        ver_bits |= (1 << 1);
                /* This doesn't handle things like foo-1.0-r0*, but that atom
-                * doesn't ever show up in practice, so who cares.
-                */
-               if (a2->PR_int)
+                * doesn't ever show up in practice, so who cares. */
+               if (query->PR_int)
                        ver_bits |= (1 << 2);
        }
 
        /* check version */
-       if (a1->PV && a2->PV) {
+       if (data->PV && query->PV) {
                char *s1, *s2;
                uint64_t n1, n2;
                /* first we compare the version [1.0]z_alpha1 */
-               s1 = a1->PV;
-               s2 = a2->PV;
+               s1 = data->PV;
+               s2 = query->PV;
                while (s1 || s2) {
                        if (s1 && s2) {
                                /* deal with leading zeros */
@@ -491,16 +567,16 @@ atom_compare(const depend_atom *a1, const depend_atom *a2)
                /* compare trailing letter 1.0[z]_alpha1 */
                if (sfx_op == ATOM_OP_STAR) {
                        ver_bits >>= 1;
-                       if (!a2->letter && !ver_bits)
+                       if (!query->letter && !ver_bits)
                                return _atom_compare_match(EQUAL, pfx_op);
                }
-               if (a1->letter < a2->letter)
+               if (data->letter < query->letter)
                        return _atom_compare_match(OLDER, pfx_op);
-               if (a1->letter > a2->letter)
+               if (data->letter > query->letter)
                        return _atom_compare_match(NEWER, pfx_op);
                /* find differing suffixes 1.0z[_alpha1] */
-               const atom_suffix *as1 = &a1->suffixes[0];
-               const atom_suffix *as2 = &a2->suffixes[0];
+               const atom_suffix *as1 = &data->suffixes[0];
+               const atom_suffix *as2 = &query->suffixes[0];
                while (as1->suffix == as2->suffix) {
                        if (as1->suffix == VER_NORM ||
                            as2->suffix == VER_NORM)
@@ -509,8 +585,8 @@ atom_compare(const depend_atom *a1, const depend_atom *a2)
                        if (as1->sint != as2->sint)
                                break;
 
-                       ++as1;
-                       ++as2;
+                       as1++;
+                       as2++;
                }
                /* compare suffixes 1.0z[_alpha]1 */
                if (sfx_op == ATOM_OP_STAR) {
@@ -530,15 +606,15 @@ atom_compare(const depend_atom *a1, const depend_atom *a2)
                else if (as1->sint > as2->sint)
                        return _atom_compare_match(NEWER, pfx_op);
                /* fall through to -r# check below */
-       } else if (a1->PV || a2->PV)
+       } else if (data->PV || query->PV)
                return EQUAL;
 
        /* Make sure the -r# is the same. */
-       if ((sfx_op == ATOM_OP_STAR && !a2->PR_int) ||
+       if ((sfx_op == ATOM_OP_STAR && !query->PR_int) ||
            pfx_op == ATOM_OP_PV_EQUAL ||
-           a1->PR_int == a2->PR_int)
+           data->PR_int == query->PR_int)
                return _atom_compare_match(EQUAL, pfx_op);
-       else if (a1->PR_int < a2->PR_int)
+       else if (data->PR_int < query->PR_int)
                return _atom_compare_match(OLDER, pfx_op);
        else
                return _atom_compare_match(NEWER, pfx_op);
@@ -586,6 +662,11 @@ atom_to_string_r(char *buf, size_t buflen, depend_atom *a)
        if (a->PR_int > 0)
                off += snprintf(buf + off, buflen - off, "-r%d", a->PR_int);
        off += snprintf(buf + off, buflen - off, "%s", atom_op_str[a->sfx_op]);
+       if (a->SLOT != NULL || a->slotdep != ATOM_SD_NONE)
+               off += snprintf(buf + off, buflen - off, ":%s%s%s%s",
+                               a->SLOT ? a->SLOT : "",
+                               a->SUBSLOT ? "/" : "", a->SUBSLOT ? a->SUBSLOT 
: "",
+                               atom_slotdep_str[a->slotdep]);
        for (ud = a->usedeps; ud != NULL; ud = ud->next)
                off += snprintf(buf + off, buflen - off, "%s%s%s%s%s",
                                ud == a->usedeps ? "[" : "",
@@ -593,11 +674,6 @@ atom_to_string_r(char *buf, size_t buflen, depend_atom *a)
                                ud->use,
                                atom_usecond_str[ud->sfx_cond],
                                ud->next == NULL ? "]" : ",");
-       if (a->SLOT != NULL)
-               off += snprintf(buf + off, buflen - off, ":%s%s%s%s",
-                               a->SLOT,
-                               a->SUBSLOT ? "/" : "", a->SUBSLOT ? a->SUBSLOT 
: "",
-                               atom_slotdep_str[a->slotdep]);
        if (a->REPO != NULL)
                off += snprintf(buf + off, buflen - off, "::%s", a->REPO);
 

diff --git a/libq/atom.h b/libq/atom.h
index 51ea88e..b24783d 100644
--- a/libq/atom.h
+++ b/libq/atom.h
@@ -19,7 +19,7 @@ extern const char * const atom_suffixes_str[];
 typedef enum {
        /*   */ ATOM_SD_NONE = 0,
        /* = */ ATOM_SD_ANY_REBUILD,
-       /* * */ ATOM_SD_ANY_IGNORE
+       /* * */ ATOM_SD_ANY_IGNORE,
 } atom_slotdep;
 extern const char * const atom_slotdep_str[];
 
@@ -38,6 +38,7 @@ typedef enum {
        /*    */ ATOM_BL_NONE = 0,
        /* !  */ ATOM_BL_BLOCK,
        /* !! */ ATOM_BL_BLOCK_HARD,
+       /* ^  */ ATOM_BL_ANTISLOT,
 } atom_blocker;
 extern const char * const atom_blocker_str[];
 

diff --git a/man/include/qatom-01-antislot.include 
b/man/include/qatom-01-antislot.include
new file mode 100644
index 0000000..65b7610
--- /dev/null
+++ b/man/include/qatom-01-antislot.include
@@ -0,0 +1,37 @@
+.SH "ANTISLOT"
+A feature present in portage-utils is the so-called antislot, and is
+activated by starting the atom with a carrot (^), in place of the
+blocker bang (!).  The antislot is similar to the inversing behaviour of
+a blocker, but only operates on SLOT and SUBSLOT, and requires SLOT to
+be available, e.g. it won't match unset SLOT (NULL).
+
+The antislot is mostly useful with dependencies and is best described
+with an example.  Consider a perl upgrade from \fIperl:0/5.28\fR to
+\fIperl:0/5.30\fR.  To find a consumer for \fIperl:0/5.28\fR a simple
+match can be made, e.g. using \fBqdepends\Rf(1):
+
+.nf
+       $ \fIqdepends -Q perl:0/5.28\fR
+       virtual/perl-ExtUtils-MakeMaker-7.340.0-r1: ... dev-lang/perl:0/5.28 ...
+       ...
+.fi
+
+However, to query after \fIperl:0/5.30\fR is installed, which packages
+are still not rebuilt (e.g. depending on an older perl: 5.28, 5.26,
+etc.) one can use the antislot:
+
+.nf
+       $ \fIqdepends -Q ^perl:0/5.30\fR
+       ...
+.fi
+
+This will return any package that depends on \fIperl:0\fR not having
+subslot \fI5.30\fR.
+.P
+Obviously this can be tested using \fBqatom\fR using the \fI-c\fR
+option:
+
+.nf
+       $ \fIqatom -c ^perl:0/5.30 perl-5.28.1:0/5.28\fR
+       ^perl:0/5.30 == perl-5.28.1:0/5.28
+.fi

diff --git a/man/include/qdepends.optdesc.yaml 
b/man/include/qdepends.optdesc.yaml
index 0c8ef6b..dc4ce91 100644
--- a/man/include/qdepends.optdesc.yaml
+++ b/man/include/qdepends.optdesc.yaml
@@ -9,7 +9,11 @@ query: |
     Query reverse deps.  This basically reverses the search to any
     package that references \fI<arg>\fR in DEPEND, RDEPEND, PDEPEND or BDEPEND.
     This can be useful to find consumers of a given package, e.g.\ to
-    search for packages that have \fIlogwatch\fR in their DEPEND.
+    search for packages that have \fIlogwatch\fR in their DEPEND.  Note
+    that using versions or range specifiers may yield odd results since
+    dependency strings often have ranges themselves.  For installed
+    packages, SLOT and SUBSLOTs are available, thus SLOT and antiSLOT
+    queries are possible.  See \fBqatom\fR(1).
 name-only: |
     Only show category/package, instead of category/package-version.
 format: |

diff --git a/man/qatom.1 b/man/qatom.1
index 2d59faa..968e1af 100644
--- a/man/qatom.1
+++ b/man/qatom.1
@@ -1,5 +1,5 @@
 .\" generated by mkman.py, please do NOT edit!
-.TH qatom "1" "May 2019" "Gentoo Foundation" "qatom"
+.TH qatom "1" "Jun 2019" "Gentoo Foundation" "qatom"
 .SH NAME
 qatom \- split atom strings
 .SH SYNOPSIS
@@ -87,7 +87,43 @@ Print this help and exit.
 .TP
 \fB\-V\fR, \fB\-\-version\fR
 Print version and exit.
+.SH "ANTISLOT"
+A feature present in portage-utils is the so-called antislot, and is
+activated by starting the atom with a carrot (^), in place of the
+blocker bang (!).  The antislot is similar to the inversing behaviour of
+a blocker, but only operates on SLOT and SUBSLOT, and requires SLOT to
+be available, e.g. it won't match unset SLOT (NULL).
 
+The antislot is mostly useful with dependencies and is best described
+with an example.  Consider a perl upgrade from \fIperl:0/5.28\fR to
+\fIperl:0/5.30\fR.  To find a consumer for \fIperl:0/5.28\fR a simple
+match can be made, e.g. using \fBqdepends\Rf(1):
+
+.nf
+       $ \fIqdepends -Q perl:0/5.28\fR
+       virtual/perl-ExtUtils-MakeMaker-7.340.0-r1: ... dev-lang/perl:0/5.28 ...
+       ...
+.fi
+
+However, to query after \fIperl:0/5.30\fR is installed, which packages
+are still not rebuilt (e.g. depending on an older perl: 5.28, 5.26,
+etc.) one can use the antislot:
+
+.nf
+       $ \fIqdepends -Q ^perl:0/5.30\fR
+       ...
+.fi
+
+This will return any package that depends on \fIperl:0\fR not having
+subslot \fI5.30\fR.
+.P
+Obviously this can be tested using \fBqatom\fR using the \fI-c\fR
+option:
+
+.nf
+       $ \fIqatom -c ^perl:0/5.30 perl-5.28.1:0/5.28\fR
+       ^perl:0/5.30 == perl-5.28.1:0/5.28
+.fi
 .SH "REPORTING BUGS"
 Please report bugs via http://bugs.gentoo.org/
 .br

diff --git a/man/qdepends.1 b/man/qdepends.1
index 277462c..d7c8957 100644
--- a/man/qdepends.1
+++ b/man/qdepends.1
@@ -1,5 +1,5 @@
 .\" generated by mkman.py, please do NOT edit!
-.TH qdepends "1" "May 2019" "Gentoo Foundation" "qdepends"
+.TH qdepends "1" "Jun 2019" "Gentoo Foundation" "qdepends"
 .SH NAME
 qdepends \- show dependency info
 .SH SYNOPSIS
@@ -56,7 +56,11 @@ Show BDEPEND info.
 Query reverse deps.  This basically reverses the search to any
 package that references \fI<arg>\fR in DEPEND, RDEPEND, PDEPEND or BDEPEND.
 This can be useful to find consumers of a given package, e.g.\ to
-search for packages that have \fIlogwatch\fR in their DEPEND.
+search for packages that have \fIlogwatch\fR in their DEPEND.  Note
+that using versions or range specifiers may yield odd results since
+dependency strings often have ranges themselves.  For installed
+packages, SLOT and SUBSLOTs are available, thus SLOT and antiSLOT
+queries are possible.  See \fBqatom\fR(1).
 .TP
 \fB\-i\fR, \fB\-\-installed\fR
 Search installed packages using VDB.

diff --git a/qatom.c b/qatom.c
index c7b6ccf..6228cbc 100644
--- a/qatom.c
+++ b/qatom.c
@@ -63,20 +63,38 @@ int qatom_main(int argc, char **argv)
                }
 
                switch (action) {
-               case _COMPARE:
+               case _COMPARE: {
+                       int r;
+
                        i++;
                        atomc = atom_explode(argv[i]);
                        if (atomc == NULL) {
                                warnf("invalid atom: %s\n", argv[i]);
                                break;
                        }
-                       printf("%s %s ",
-                                       atom_to_string(atom),
-                                       booga[atom_compare(atom, atomc)]);
-                       printf("%s\n",
-                                       atom_to_string(atomc));
+
+                       if (atomc->blocker != ATOM_BL_NONE ||
+                                       atomc->pfx_op != ATOM_OP_NONE ||
+                                       atomc->sfx_op != ATOM_OP_NONE ||
+                                       (atomc->CATEGORY == NULL &&
+                                        atom->blocker == ATOM_BL_NONE &&
+                                        atom->pfx_op == ATOM_OP_NONE &&
+                                        atom->sfx_op == ATOM_OP_NONE))
+                       {
+                               r = atom_compare(atom, atomc);
+                       } else {
+                               r = atom_compare(atomc, atom);
+                               switch (r) {
+                                       case NEWER:     r = OLDER;     break;
+                                       case OLDER:     r = NEWER;     break;
+                               }
+                       }
+
+                       printf("%s %s ", atom_to_string(atom), booga[r]);
+                       printf("%s\n", atom_to_string(atomc));
                        atom_implode(atomc);
                        break;
+               }
                case _EXPLODE:
                        printf("%s\n", atom_format(format, atom, verbose));
                        break;

diff --git a/qdepends.c b/qdepends.c
index f2ae111..f560c31 100644
--- a/qdepends.c
+++ b/qdepends.c
@@ -132,7 +132,11 @@ qdepends_results_cb(tree_pkg_ctx *pkg_ctx, void *priv)
        if ((state->qmode & QMODE_REVERSE) == 0) {
                /* see if this cat/pkg is requested */
                array_for_each(state->atoms, i, atom) {
-                       if (atom_compare(atom, datom) == EQUAL) {
+                       if (atom->blocker != ATOM_BL_NONE ||
+                                       atom->SLOT != NULL ||
+                                       atom->REPO != NULL)
+                               datom = tree_get_atom(pkg_ctx, true);
+                       if (atom_compare(datom, atom) == EQUAL) {
                                atom = NULL;
                                break;
                        }
@@ -203,7 +207,7 @@ qdepends_results_cb(tree_pkg_ctx *pkg_ctx, void *priv)
                        if (state->qmode & QMODE_REVERSE) {
                                array_for_each(state->atoms, m, atom) {
                                        array_for_each(state->deps, n, fatom) {
-                                               if (atom_compare(atom, fatom) 
== EQUAL) {
+                                               if (atom_compare(fatom, atom) 
== EQUAL) {
                                                        fatom = NULL;
                                                        break;
                                                }

diff --git a/tests/qatom/dotest b/tests/qatom/dotest
index 9453f2d..d6b4943 100755
--- a/tests/qatom/dotest
+++ b/tests/qatom/dotest
@@ -76,9 +76,13 @@ test c08 "cat/pkg-123:foo != cat/pkg-123:bar" \
        -c 'cat/pkg-123:foo' 'cat/pkg-123:bar'
 test c09 "cat/pkg-123:foo == cat/pkg-123:foo=" \
        -c 'cat/pkg-123:foo' 'cat/pkg-123:foo='
-test c10 "cat/pkg-123:foo == cat/pkg-123:=" \
-       -c 'cat/pkg-123:foo' 'cat/pkg-123:='
+test c10 "cat/pkg-123:foo != cat/pkg-123:=" \
+       -c 'cat/pkg-123:foo' 'cat/pkg-123:='            # slot vs empty slot
 test c11 "cat/pkg-123[!foo,bar(+),baz=] == cat/pkg-123" \
        -c 'cat/pkg-123[!foo,bar(+),baz=]' 'cat/pkg-123'
+test c12 "cat/pkg-123:0/25 == ^cat/pkg-123:0/26" \
+       -c 'cat/pkg-123:0/25' '^cat/pkg-123:0/26'       # bug 683430 antislot
+test c13 "cat/pkg-123 != ^cat/pkg-123:0/26" \
+       -c 'cat/pkg-123' '^cat/pkg-123:0/26'
 
 end

Reply via email to