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 - <so...@gentoo.org> * Copyright 2005-2016 Mike Frysinger - <vap...@gentoo.org> + * Copyright 2019- Fabian Groffen - <grob...@gentoo.org> */ #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