commit: 199e4f62b4ede5f2319ef6795a15737ac0882d09
Author: Fabian Groffen <grobian <AT> gentoo <DOT> org>
AuthorDate: Sun Apr 7 20:06:20 2019 +0000
Commit: Fabian Groffen <grobian <AT> gentoo <DOT> org>
CommitDate: Sun Apr 7 20:14:35 2019 +0000
URL: https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=199e4f62
atoms: add proper support for blockers and USE dependencies
- ignore SLOT in atom_compare when not set on both sides
- parse USE-dependencies, properly removing it from PV
- parse blockers (! and !!) separate from version ranges, such that the
original meaning can be restored and differentiated from
Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org>
libq/atom.c | 250 ++++++++++++++++++++++++++++-----------
libq/atom.h | 66 ++++++++---
man/qatom.1 | 5 +-
qatom.c | 40 +++++--
tests/atom_compare/static.good | 4 +-
tests/atom_compare/static.q.good | 2 +-
tests/atom_compare/static.tests | 4 +-
tests/qatom/dotest | 22 ++++
8 files changed, 298 insertions(+), 95 deletions(-)
diff --git a/libq/atom.c b/libq/atom.c
index 60a37ce..4c06c1a 100644
--- a/libq/atom.c
+++ b/libq/atom.c
@@ -16,16 +16,28 @@
#include <ctype.h>
#include <xalloc.h>
-const char * const booga[] = {"!!!", "!=", "==", ">", "<"};
-
const char * const atom_suffixes_str[] = {
"_alpha", "_beta", "_pre", "_rc", "_/*bogus*/", "_p"
};
+const char * const atom_slotdep_str[] = {
+ "", "=", "*"
+};
+
+const char * const atom_usecond_str[] = {
+ "", "!", "-", "?", "=", "(+)", "(-)"
+};
+
+const char * const atom_blocker_str[] = {
+ "", "!", "!!"
+};
+
const char * const atom_op_str[] = {
- "", ">", ">=", "=", "<=", "<", "~", "!", "!!", "*"
+ "", "=", ">", ">=", "<", "<=", "~", "*"
};
+const char * const booga[] = {"!!!", "!=", "==", ">", "<"};
+
#ifdef EBUG
void
atom_print(const depend_atom *atom)
@@ -52,7 +64,10 @@ atom_explode(const char *atom)
{
depend_atom *ret;
char *ptr;
- size_t len, slen, idx, sidx;
+ size_t len;
+ size_t slen;
+ size_t idx;
+ size_t sidx;
/* we allocate mem for atom struct and two strings (strlen(atom)).
* the first string is for CAT/PN/PV while the second is for PVR.
@@ -76,61 +91,37 @@ atom_explode(const char *atom)
ret->PVR = ret->P + slen + 1;
ret->CATEGORY = ret->PVR + slen + 1 + 3;
+ /* check for blocker operators */
+ ret->blocker = ATOM_BL_NONE;
+ if (*atom == '!') {
+ ret->blocker++;
+ atom++;
+ }
+ if (*atom == '!') {
+ ret->blocker++;
+ atom++;
+ }
+
/* eat any prefix operators */
- switch (atom[0]) {
+ ret->pfx_op = ATOM_OP_NONE;
+ switch (*atom) {
case '>':
- ++atom;
- if (atom[0] == '=') {
- ++atom;
- ret->pfx_op = ATOM_OP_NEWER_EQUAL;
- } else
- ret->pfx_op = ATOM_OP_NEWER;
- break;
- case '=':
- ++atom;
- ret->pfx_op = ATOM_OP_EQUAL;
+ ret->pfx_op = ATOM_OP_NEWER;
+ atom++;
break;
case '<':
- ++atom;
- if (atom[0] == '=') {
- ++atom;
- ret->pfx_op = ATOM_OP_OLDER_EQUAL;
- } else
- ret->pfx_op = ATOM_OP_OLDER;
+ ret->pfx_op = ATOM_OP_OLDER;
+ atom++;
break;
case '~':
- ++atom;
ret->pfx_op = ATOM_OP_PV_EQUAL;
- break;
- case '!':
- ++atom;
- switch (atom[0]) {
- case '!':
- ++atom;
- ret->pfx_op = ATOM_OP_BLOCK_HARD;
- break;
- case '>':
- ++atom;
- if (atom[0] == '=') {
- ++atom;
- ret->pfx_op = ATOM_OP_OLDER;
- } else
- ret->pfx_op = ATOM_OP_OLDER_EQUAL;
- break;
- case '<':
- ++atom;
- if (atom[0] == '=') {
- ++atom;
- ret->pfx_op = ATOM_OP_NEWER;
- } else
- ret->pfx_op = ATOM_OP_NEWER_EQUAL;
- break;
- default:
- ret->pfx_op = ATOM_OP_BLOCK;
- break;
- }
+ atom++;
break;
}
+ if (*atom == '=') {
+ ret->pfx_op += ATOM_OP_EQUAL;
+ atom++;
+ }
strcpy(ret->CATEGORY, atom);
/* eat file name crap when given an (autocompleted) path */
@@ -145,21 +136,24 @@ atom_explode(const char *atom)
/* chip off the trailing [:SLOT] as needed */
if ((ptr = strrchr(ret->CATEGORY, ':')) != NULL) {
- ret->SLOT = ptr + 1;
- *ptr = '\0';
+ *ptr++ = '\0';
+ ret->SLOT = ptr;
- /* ignore slots that are about package matching */
- if (ret->SLOT[0] == '=' || ret->SLOT[0] == '*')
- ret->SLOT = NULL;
+ /* 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';
+ }
}
/* see if we have any suffix operators */
- if ((ptr = strrchr(ret->CATEGORY, '*')) != NULL) {
- /* make sure it's the last byte */
- if (ptr[1] == '\0') {
- ret->sfx_op = ATOM_OP_STAR;
- *ptr = '\0';
- }
+ if ((ptr = strrchr(ret->CATEGORY, '*')) != NULL && ptr[1] == '\0') {
+ ret->sfx_op = ATOM_OP_STAR;
+ *ptr = '\0';
}
/* break up the CATEGORY and PVR */
@@ -167,7 +161,8 @@ atom_explode(const char *atom)
ret->PN = ptr + 1;
*ptr = '\0';
/* eat extra crap in case it exists, this is a feature to allow
- * /path/to/pkg.ebuild */
+ * /path/to/pkg.ebuild, doesn't work with prefix operators
+ * though */
if ((ptr = strrchr(ret->CATEGORY, '/')) != NULL)
ret->CATEGORY = ptr + 1;
} else {
@@ -175,6 +170,68 @@ atom_explode(const char *atom)
ret->CATEGORY = NULL;
}
+ /* hunt down build with USE dependencies */
+ if ((ptr = strrchr(ret->PN, ']')) != NULL && ptr[1] == '\0' &&
+ (ptr = strrchr(ret->PN, '[')) != NULL)
+ {
+ atom_usedep *w = NULL;
+ do {
+ if (ret->usedeps == NULL) {
+ ret->usedeps = w = xmalloc(sizeof(atom_usedep));
+ } else {
+ w = w->next = xmalloc(sizeof(atom_usedep));
+ }
+ w->next = NULL;
+ *ptr++ = '\0';
+ w->pfx_cond = w->sfx_cond = ATOM_UC_NONE;
+ switch (*ptr) {
+ case '-':
+ w->pfx_cond = ATOM_UC_NEG;
+ ptr++;
+ break;
+ case '!':
+ w->pfx_cond = ATOM_UC_NOT;
+ ptr++;
+ break;
+ }
+ w->use = ptr;
+ while (*ptr != '\0') {
+ switch (*ptr) {
+ case '?':
+ w->sfx_cond = ATOM_UC_COND;
+ *ptr++ = '\0';
+ break;
+ case '=':
+ w->sfx_cond = ATOM_UC_EQUAL;
+ *ptr++ = '\0';
+ break;
+ case '(':
+ if (strncmp(ptr, "(+)", 3) ==
0) {
+ w->sfx_cond =
ATOM_UC_PREV_ENABLED;
+ *ptr = '\0';
+ ptr += 3;
+ } else if (strncmp(ptr, "(-)",
3) == 0) {
+ w->sfx_cond =
ATOM_UC_PREV_ENABLED;
+ *ptr = '\0';
+ ptr += 3;
+ } else {
+ ptr++;
+ }
+ break;
+ case ',':
+ case ']':
+ *ptr = ']';
+ break;
+ default:
+ ptr++;
+ }
+ if (*ptr == ']')
+ break;
+ }
+ } while (ptr[1] != '\0');
+ *ptr++ = '\0';
+ }
+
/* 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
@@ -202,7 +259,7 @@ atom_explode(const char *atom)
/* allow for 1 optional suffix letter */
if (*ptr >= 'a' && *ptr <= 'z')
ret->letter = *ptr++;
- if (*ptr == '_' || *ptr == '\0' || *ptr == '-') {
+ if (*ptr == '_' || *ptr == '-' || *ptr == '\0') {
lastpv = pv;
continue; /* valid, keep searching */
}
@@ -333,12 +390,35 @@ atom_compare(const depend_atom *a1, const depend_atom *a2)
atom_operator pfx_op = a2->pfx_op;
atom_operator sfx_op = a2->sfx_op;
- /* check slot */
- if (a1->SLOT || a2->SLOT) {
- if (!a1->SLOT || !a2->SLOT || strcmp(a1->SLOT, a2->SLOT))
- return NOT_EQUAL;
+ /* handle the inversing effect of blockers */
+ if (a2->blocker != ATOM_BL_NONE) {
+ switch (pfx_op) {
+ case ATOM_OP_NEWER:
+ pfx_op = ATOM_OP_OLDER_EQUAL;
+ break;
+ case ATOM_OP_NEWER_EQUAL:
+ pfx_op = ATOM_OP_OLDER;
+ break;
+ case ATOM_OP_OLDER:
+ pfx_op = ATOM_OP_NEWER_EQUAL;
+ break;
+ case ATOM_OP_OLDER_EQUAL:
+ pfx_op = ATOM_OP_NEWER;
+ break;
+ case ATOM_OP_EQUAL:
+ case ATOM_OP_PV_EQUAL:
+ default:
+ pfx_op = ATOM_OP_NEQUAL;
+ break;
+ }
}
+ /* check slot only when both sides have it */
+ if (a1->SLOT && a2->SLOT &&
+ a1->SLOT[0] != '\0' && a2->SLOT[0] != '\0' &&
+ strcmp(a1->SLOT, a2->SLOT) != 0)
+ return NOT_EQUAL;
+
/* check repo */
if (a1->REPO || a2->REPO) {
if (!a1->REPO || !a2->REPO || strcmp(a1->REPO, a2->REPO))
@@ -489,3 +569,39 @@ implode_a1_ret:
atom_implode(a1);
return ret;
}
+
+static char _atom_buf[BUFSIZ];
+char *
+atom_to_string(depend_atom *a)
+{
+ char *buf = _atom_buf;
+ size_t buflen = sizeof(_atom_buf);
+ size_t off = 0;
+ atom_usedep *ud;
+
+ off += snprintf(buf + off, buflen - off, "%s%s",
+ atom_blocker_str[a->blocker], atom_op_str[a->pfx_op]);
+ if (a->CATEGORY != NULL)
+ off += snprintf(buf + off, buflen - off, "%s/", a->CATEGORY);
+ if (a->PN != NULL)
+ off += snprintf(buf + off, buflen - off, "%s", a->PN);
+ if (a->PV != NULL)
+ off += snprintf(buf + off, buflen - off, "-%s", a->PV);
+ 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]);
+ for (ud = a->usedeps; ud != NULL; ud = ud->next)
+ off += snprintf(buf + off, buflen - off, "%s%s%s%s%s",
+ ud == a->usedeps ? "[" : "",
+ atom_usecond_str[ud->pfx_cond],
+ ud->use,
+ atom_usecond_str[ud->sfx_cond],
+ ud->next == NULL ? "]" : ",");
+ if (a->SLOT != NULL)
+ off += snprintf(buf + off, buflen - off, ":%s%s",
+ a->SLOT, atom_slotdep_str[a->slotdep]);
+ if (a->REPO != NULL)
+ off += snprintf(buf + off, buflen - off, "::%s", a->REPO);
+
+ return buf;
+}
diff --git a/libq/atom.h b/libq/atom.h
index c8e900f..c9a1ddb 100644
--- a/libq/atom.h
+++ b/libq/atom.h
@@ -4,6 +4,7 @@
*
* Copyright 2005-2008 Ned Ludd - <[email protected]>
* Copyright 2005-2016 Mike Frysinger - <[email protected]>
+ * Copyright 2019- Fabian Groffen - <[email protected]>
*/
#ifndef _ATOM_COMPARE_H
@@ -12,39 +13,75 @@
typedef enum {
VER_ALPHA=0, VER_BETA, VER_PRE, VER_RC, VER_NORM, VER_P
} atom_suffixes;
-
extern const char * const atom_suffixes_str[];
-typedef struct {
- atom_suffixes suffix;
- uint64_t sint;
-} atom_suffix;
+/* slotdeps, := :* :SLOT= */
+typedef enum {
+ /* */ ATOM_SD_NONE = 0,
+ /* = */ ATOM_SD_ANY_REBUILD,
+ /* * */ ATOM_SD_ANY_IGNORE
+} atom_slotdep;
+extern const char * const atom_slotdep_str[];
-extern const char * const atom_op_str[];
+typedef enum {
+ /* */ ATOM_UC_NONE = 0,
+ /* ! */ ATOM_UC_NOT,
+ /* - */ ATOM_UC_NEG,
+ /* ? */ ATOM_UC_COND,
+ /* = */ ATOM_UC_EQUAL,
+ /* (+) */ ATOM_UC_PREV_ENABLED,
+ /* (-) */ ATOM_UC_PREV_DISABLED,
+} atom_usecond;
+extern const char * const atom_usecond_str[];
+
+typedef enum {
+ /* */ ATOM_BL_NONE = 0,
+ /* ! */ ATOM_BL_BLOCK,
+ /* !! */ ATOM_BL_BLOCK_HARD,
+} atom_blocker;
+extern const char * const atom_blocker_str[];
typedef enum {
/* */ ATOM_OP_NONE = 0,
+ /* = */ ATOM_OP_EQUAL,
/* > */ ATOM_OP_NEWER,
/* >= */ ATOM_OP_NEWER_EQUAL,
- /* = */ ATOM_OP_EQUAL,
- /* <= */ ATOM_OP_OLDER_EQUAL,
/* < */ ATOM_OP_OLDER,
+ /* <= */ ATOM_OP_OLDER_EQUAL,
/* ~ */ ATOM_OP_PV_EQUAL,
- /* ! */ ATOM_OP_BLOCK,
- /* !! */ ATOM_OP_BLOCK_HARD,
/* * */ ATOM_OP_STAR,
+ /* */ ATOM_OP_NEQUAL,
} atom_operator;
+extern const char * const atom_op_str[];
+
+typedef struct {
+ atom_suffixes suffix;
+ uint64_t sint;
+} atom_suffix;
+
+typedef struct _atom_usedep {
+ struct _atom_usedep *next;
+ char *use;
+ atom_usecond pfx_cond;
+ atom_usecond sfx_cond;
+} atom_usedep;
typedef struct {
- /* XXX: we don't provide PF ... */
- atom_operator pfx_op, sfx_op;
+ atom_blocker blocker;
+ atom_operator pfx_op;
+ atom_operator sfx_op;
char *CATEGORY;
char *PN;
+ char *PV;
unsigned int PR_int;
char letter;
atom_suffix *suffixes;
- char *PV, *PVR;
- char *P, *SLOT, *REPO;
+ char *PVR;
+ char *P;
+ atom_usedep *usedeps;
+ char *SLOT;
+ atom_slotdep slotdep;
+ char *REPO;
} depend_atom;
extern const char * const booga[];
@@ -54,5 +91,6 @@ depend_atom *atom_explode(const char *atom);
void atom_implode(depend_atom *atom);
int atom_compare(const depend_atom *a1, const depend_atom *a2);
int atom_compare_str(const char * const s1, const char * const s2);
+char *atom_to_string(depend_atom *a);
#endif
diff --git a/man/qatom.1 b/man/qatom.1
index 4860957..046b89e 100644
--- a/man/qatom.1
+++ b/man/qatom.1
@@ -1,5 +1,5 @@
.\" generated by mkman.py, please do NOT edit!
-.TH qatom "1" "Feb 2019" "Gentoo Foundation" "qatom"
+.TH qatom "1" "Apr 2019" "Gentoo Foundation" "qatom"
.SH NAME
qatom \- split atom strings
.SH SYNOPSIS
@@ -67,6 +67,9 @@ The package suffices, currently that is just the asterisk.
\fB\-c\fR, \fB\-\-compare\fR
Compare two atoms.
.TP
+\fB\-p\fR, \fB\-\-print\fR
+Print reconstructed atom.
+.TP
\fB\-\-root\fR \fI<arg>\fR
Set the ROOT env var.
.TP
diff --git a/qatom.c b/qatom.c
index c1af10d..20e2dcb 100644
--- a/qatom.c
+++ b/qatom.c
@@ -16,15 +16,17 @@
#define QATOM_FORMAT "%{CATEGORY} %{PN} %{PV} %[PR] %[SLOT] %[pfx] %[sfx]"
-#define QATOM_FLAGS "F:c" COMMON_FLAGS
+#define QATOM_FLAGS "F:cp" COMMON_FLAGS
static struct option const qatom_long_opts[] = {
{"format", a_argument, NULL, 'F'},
{"compare", no_argument, NULL, 'c'},
+ {"print", no_argument, NULL, 'p'},
COMMON_LONG_OPTS
};
static const char * const qatom_opts_help[] = {
"Custom output format (default: " QATOM_FORMAT ")",
"Compare two atoms",
+ "Print reconstructed atom",
COMMON_OPTS_HELP
};
#define qatom_usage(ret) usage(ret, QATOM_FLAGS, qatom_long_opts,
qatom_opts_help, NULL, lookup_applet_idx("qatom"))
@@ -92,7 +94,9 @@ qatom_printf(const char *format, const depend_atom *atom, int
pverbose)
printf("r%i", atom->PR_int);
} else if (!strncmp("SLOT", fmt, len)) {
if (showit || atom->SLOT)
- printf(":%s", atom->SLOT ?
atom->SLOT : "-");
+ printf(":%s%s",
+ atom->SLOT ?
atom->SLOT : "-",
+
atom_slotdep_str[atom->slotdep]);
} else if (!strncmp("REPO", fmt, len)) {
if (showit || atom->REPO)
printf("::%s", HN(atom->REPO));
@@ -117,15 +121,17 @@ qatom_printf(const char *format, const depend_atom *atom,
int pverbose)
int qatom_main(int argc, char **argv)
{
- enum qatom_atom { _EXPLODE=0, _COMPARE } action = _EXPLODE;
+ enum qatom_atom { _EXPLODE=0, _COMPARE, _PRINT } action = _EXPLODE;
const char *format = QATOM_FORMAT;
depend_atom *atom;
+ depend_atom *atomc;
int i;
while ((i = GETOPT_LONG(QATOM, qatom, "")) != -1) {
switch (i) {
case 'F': format = optarg; break;
case 'c': action = _COMPARE; break;
+ case 'p': action = _PRINT; break;
COMMON_GETOPTS_CASES(qatom)
}
}
@@ -137,19 +143,37 @@ int qatom_main(int argc, char **argv)
err("compare needs even number of arguments");
for (i = optind; i < argc; ++i) {
+ atom = atom_explode(argv[i]);
+ if (atom == NULL) {
+ warnf("invalid atom: %s\n", argv[i]);
+ continue;
+ }
+
switch (action) {
case _COMPARE:
- printf("%s %s %s\n", argv[i],
- booga[atom_compare_str(argv[i],
argv[i+1])], argv[i+1]);
- ++i;
+ 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));
+ atom_implode(atomc);
break;
case _EXPLODE:
- atom = atom_explode(argv[i]);
qatom_printf(format, atom, verbose);
putchar('\n');
- atom_implode(atom);
+ break;
+ case _PRINT:
+ printf("%s\n", atom_to_string(atom));
break;
}
+
+ atom_implode(atom);
}
return EXIT_SUCCESS;
diff --git a/tests/atom_compare/static.good b/tests/atom_compare/static.good
index 9061ae4..6da5553 100644
--- a/tests/atom_compare/static.good
+++ b/tests/atom_compare/static.good
@@ -10,7 +10,7 @@ a-2.0_pre < a-2.0_rc
a-2.0_pre < a-2.0_p1234
a-2.0_rc < a-2.0
a-1z > a-1b
-a-1-r0 < a-1-r1
+a-1 < a-1-r1
a-1d_p1-r12 < a-1d_p1-r50
a-1.034 < a-1.1
a-1.0.1 < a-1.002
@@ -40,7 +40,7 @@ a-1 == <=a-2
a-1 != ~a-0
a-1 == ~a-1
a-1 != ~a-2
-a-1-r1 == ~a-1-r0
+a-1-r1 == ~a-1
a-1-r1 == ~a-1-r1
a-1-r1 == ~a-1-r2
a-1 == =a-1*
diff --git a/tests/atom_compare/static.q.good b/tests/atom_compare/static.q.good
index 08a6635..621cf0b 100644
--- a/tests/atom_compare/static.q.good
+++ b/tests/atom_compare/static.q.good
@@ -1,7 +1,7 @@
a/b == b
a/b-1 == b
a-1 == =a-1.0*
-a-1:1234 != a-1
+a-1:1234 == a-1
a-1:0 < a-2:0
a-1:0 == a-1:0
a-1_alpha1 != =a-1-r1*
diff --git a/tests/atom_compare/static.tests b/tests/atom_compare/static.tests
index 2f081c8..168f358 100644
--- a/tests/atom_compare/static.tests
+++ b/tests/atom_compare/static.tests
@@ -10,7 +10,7 @@ a-2.0_pre a-2.0_rc
a-2.0_pre a-2.0_p1234
a-2.0_rc a-2.0
a-1z a-1b
-a-1-r0 a-1-r1
+a-1 a-1-r1
a-1d_p1-r12 a-1d_p1-r50
a-1.034 a-1.1
a-1.0.1 a-1.002
@@ -40,7 +40,7 @@ a-1 <=a-2
a-1 ~a-0
a-1 ~a-1
a-1 ~a-2
-a-1-r1 ~a-1-r0
+a-1-r1 ~a-1
a-1-r1 ~a-1-r1
a-1-r1 ~a-1-r2
a-1 =a-1*
diff --git a/tests/qatom/dotest b/tests/qatom/dotest
index c879624..2e16b25 100755
--- a/tests/qatom/dotest
+++ b/tests/qatom/dotest
@@ -55,4 +55,26 @@ test f17 "games-rpg eschalon-book-1-demo 106 r1" \
-F '%{CATEGORY} %{PN} %{PV} %{PR}' \
"games-rpg/eschalon-book-1-demo-106-r1"
+# Comparison tests
+test c01 "cat/pkg-123-r3 == pkg" \
+ -c 'cat/pkg-123-r3' 'pkg'
+test c02 "cat/pkg-123-r3 == !<cat/pkg-123" \
+ -c 'cat/pkg-123-r3' '!<cat/pkg-123'
+test c03 "cat/pkg-123-r3 != !<cat/pkg-124" \
+ -c 'cat/pkg-123-r3' '!<cat/pkg-124'
+test c04 "cat/pkg-123-r3 < cat/pkg-123-r4" \
+ -c 'cat/pkg-123-r3' 'cat/pkg-123-r4'
+test c05 "cat/pkg-123 > cat/pkg-12.3" \
+ -c 'cat/pkg-123' 'cat/pkg-12.3'
+test c07 "cat/pkg-123 == cat/pkg-123:bar" \
+ -c 'cat/pkg-123' 'cat/pkg-123:bar' # bug 668418
+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 c11 "cat/pkg-123[!foo,bar(+),baz=] == cat/pkg-123" \
+ -c 'cat/pkg-123[!foo,bar(+),baz=]' 'cat/pkg-123'
+
end