aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabian Groffen <grobian@gentoo.org>2019-06-09 10:39:03 +0200
committerFabian Groffen <grobian@gentoo.org>2019-06-09 10:39:03 +0200
commit9fefd6f786ebb4d546a43e1b600659361efd3cb3 (patch)
tree82a559efc7e710c7350e9447a3bab50d1a6b6c95
parentqgrep: plug filedescritor leak (diff)
downloadportage-utils-9fefd6f7.tar.gz
portage-utils-9fefd6f7.tar.bz2
portage-utils-9fefd6f7.zip
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@gentoo.org>
-rw-r--r--libq/atom.c338
-rw-r--r--libq/atom.h3
-rw-r--r--man/include/qatom-01-antislot.include37
-rw-r--r--man/include/qdepends.optdesc.yaml6
-rw-r--r--man/qatom.138
-rw-r--r--man/qdepends.18
-rw-r--r--qatom.c30
-rw-r--r--qdepends.c8
-rwxr-xr-xtests/qatom/dotest8
9 files changed, 330 insertions, 146 deletions
diff --git a/libq/atom.c b/libq/atom.c
index 8b1b47a3..12a393a3 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 51ea88e4..b24783dd 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 00000000..65b7610f
--- /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 0c8ef6b4..dc4ce91c 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 2d59faae..968e1af5 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 277462ce..d7c89575 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 c7b6ccf4..6228cbcd 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 f2ae1119..f560c318 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 9453f2d3..d6b4943a 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