commit:     89a388678c4975b541d24931b0916e2959ed70be
Author:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
AuthorDate: Thu Apr  5 12:10:26 2018 +0000
Commit:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
CommitDate: Thu Apr  5 12:10:26 2018 +0000
URL:        https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=89a38867

atom_explode: fix parsing of some valid corner cases

Extract the version part of the atom, such that we can more reliably
parse the version components without worrying about accidentially taking
parts of the package name.

Bug: https://bugs.gentoo.org/526596

 libq/atom_explode.c | 78 +++++++++++++++++++++++++++++------------------------
 tests/qatom/dotest  | 13 ++++++---
 2 files changed, 53 insertions(+), 38 deletions(-)

diff --git a/libq/atom_explode.c b/libq/atom_explode.c
index 1b81909..e52f149 100644
--- a/libq/atom_explode.c
+++ b/libq/atom_explode.c
@@ -147,7 +147,7 @@ atom_explode(const char *atom)
        }
        strcpy(ret->CATEGORY, atom);
 
-       /* eat file name crap */
+       /* eat file name crap when given an (autocompleted) path */
        if ((ptr = strstr(ret->CATEGORY, ".ebuild")) != NULL)
                *ptr = '\0';
 
@@ -172,11 +172,12 @@ atom_explode(const char *atom)
                }
        }
 
-       /* break up the CATEOGRY and PVR */
+       /* break up the CATEGORY and PVR */
        if ((ptr = strrchr(ret->CATEGORY, '/')) != NULL) {
                ret->PN = ptr + 1;
                *ptr = '\0';
-               /* eat extra crap in case it exists */
+               /* eat extra crap in case it exists, this is a feature to allow
+                * /path/to/pkg.ebuild */
                if ((ptr = strrchr(ret->CATEGORY, '/')) != NULL)
                        ret->CATEGORY = ptr + 1;
        } else {
@@ -184,9 +185,28 @@ atom_explode(const char *atom)
                ret->CATEGORY = NULL;
        }
 
+       /* 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
+        * syntax, version syntax starts with a number, so "-[0-9]" is a
+        * separator from PN to PV* */
+
+       ptr = ret->PN;
+       while ((ptr = strchr(ptr, '-')) != NULL) {
+               ptr++;
+               if (*ptr >= '0' && *ptr <= '9')
+                       break;
+       }
+
+       if (ptr == NULL) {
+               /* atom has no version, this is it */
+               return ret;
+       }
+       ret->PV = ptr;
+
        /* find -r# */
-       ptr = ret->PN + strlen(ret->PN) - 1;
-       while (*ptr && ptr > ret->PN) {
+       ptr = ret->PV + strlen(ret->PV) - 1;
+       while (*ptr && ptr > ret->PV) {
                if (!isdigit(*ptr)) {
                        if (ptr[0] == 'r' && ptr[-1] == '-') {
                                ret->PR_int = atoi(ptr + 1);
@@ -197,39 +217,35 @@ atom_explode(const char *atom)
                --ptr;
        }
        strcpy(ret->P, ret->PN);
+       ret->PV[-1] = '\0';
 
        /* break out all the suffixes */
        sidx = 0;
        ret->suffixes = xrealloc(ret->suffixes, sizeof(atom_suffix) * (sidx + 
1));
        ret->suffixes[sidx].sint = 0;
        ret->suffixes[sidx].suffix = VER_NORM;
-       while ((ptr = strrchr(ret->PN, '_')) != NULL) {
+       ptr = ret->PV + strlen(ret->PV) - 1;
+       while (ptr-- > ret->PV) {
+               if (*ptr != '_')
+                       continue;
                for (idx = 0; idx < ARRAY_SIZE(atom_suffixes_str); ++idx) {
-                       if (strncmp(ptr, atom_suffixes_str[idx], 
strlen(atom_suffixes_str[idx])))
+                       if (strncmp(ptr, atom_suffixes_str[idx],
+                                               strlen(atom_suffixes_str[idx])))
                                continue;
 
-                       /* check this is a real suffix and not _p hitting 
mod_perl */
-                       char *tmp_ptr = ptr;
-                       tmp_ptr += strlen(atom_suffixes_str[idx]);
-                       ret->suffixes[sidx].sint = atoll(tmp_ptr);
-                       while (isdigit(*tmp_ptr))
-                               ++tmp_ptr;
-                       if (*tmp_ptr)
-                               goto no_more_suffixes;
+                       ret->suffixes[sidx].sint =
+                               atoll(ptr + strlen(atom_suffixes_str[idx]));
                        ret->suffixes[sidx].suffix = idx;
 
                        ++sidx;
-                       *ptr = '\0';
 
-                       ret->suffixes = xrealloc(ret->suffixes, 
sizeof(atom_suffix) * (sidx + 1));
+                       ret->suffixes = xrealloc(ret->suffixes,
+                                       sizeof(atom_suffix) * (sidx + 1));
                        ret->suffixes[sidx].sint = 0;
                        ret->suffixes[sidx].suffix = VER_NORM;
                        break;
                }
-               if (*ptr)
-                       break;
        }
- no_more_suffixes:
        if (sidx)
                --sidx;
        for (idx = 0; idx < sidx; ++idx, --sidx) {
@@ -238,35 +254,27 @@ atom_explode(const char *atom)
                ret->suffixes[idx] = t;
        }
 
-       /* allow for 1 optional suffix letter, must be following a number
-        * otherwise we eat stuff like -c, see bug #639978 */
-       ptr = ret->PN + strlen(ret->PN);
+       /* allow for 1 optional suffix letter, must be following a number */
+       ptr = ret->PV + strlen(ret->PV);
        if (ptr[-1] >= 'a' && ptr[-1] <= 'z' &&
-                       ptr - 2 > ret->PN && ptr[-2] >= '0' && ptr[-2] <= '9')
+                       ptr - 2 > ret->PV && ptr[-2] >= '0' && ptr[-2] <= '9')
        {
                ret->letter = ptr[-1];
                --ptr;
        }
 
        /* eat the trailing version number [-.0-9]+ */
-       bool has_pv = false;
-       while (--ptr > ret->PN)
+       while (--ptr > ret->PV) {
                if (*ptr == '-') {
-                       has_pv = true;
                        *ptr = '\0';
                        break;
                } else if (*ptr != '.' && !isdigit(*ptr))
                        break;
-       if (has_pv) {
-               ret->PV = ret->P + (ptr - ret->PN) + 1;
-               ptr = stpcpy(ret->PVR, ret->PV);
-               sprintf(ptr, "-r%i", ret->PR_int);
-       } else {
-               /* atom has no version */
-               ret->PV = ret->PVR = NULL;
-               ret->letter = 0;
        }
 
+       ptr = stpcpy(ret->PVR, ret->PV);
+       sprintf(ptr, "-r%i", ret->PR_int);
+
        return ret;
 }
 

diff --git a/tests/qatom/dotest b/tests/qatom/dotest
index 4bb0460..a0e6a34 100755
--- a/tests/qatom/dotest
+++ b/tests/qatom/dotest
@@ -29,9 +29,9 @@ test l07 "cat pkg 123 >=" ">=cat/pkg-123"
 test l07 "cat pkg 123 = *" "=cat/pkg-123*"
 
 # Explicit format.
-test f01 "cat"    -F '%{CATEGORY}' "cat/pkg"
-test f02 "<unset>" -F '%{CATEGORY}' "pkg"
-test f03 ""       -F '%[CATEGORY]' "pkg"
+test f01 "cat"        -F '%{CATEGORY}' "cat/pkg"
+test f02 "<unset>"    -F '%{CATEGORY}' "pkg"
+test f03 ""           -F '%[CATEGORY]' "pkg"
 test f04 "cat"        -F '%{CATEGORY}' "cat/pkg-123-r4:5"
 test f05 "pkg-123"    -F '%{P}'        "cat/pkg-123-r4:5"
 test f06 "pkg"        -F '%{PN}'       "cat/pkg-123-r4:5"
@@ -41,5 +41,12 @@ test f09 "pkg-123-r4" -F '%{PF}'       "cat/pkg-123-r4:5"
 test f10 "r4"         -F '%{PR}'       "cat/pkg-123-r4:5"
 test f11 ":5"         -F '%{SLOT}'     "cat/pkg-123-r4:5"
 test f12 "pkg-c"      -F '%{PN}'       "cat/pkg-c"  # bug #639978
+test f13 "mod_perl 1.5_p20180304 r5" \
+                      -F '%{PN} %{PV} %{PR}' \
+                                       "dev-libs/mod_perl-1.5_p20180304-r5"
+test f14 "foo-r1"     -F '%{PN}'       "foo-r1"     # bug #526596
+test f15 "app-emacs diff-mode-" \
+                      -F '%{CATEGORY} %{PN}' \
+                                       "app-emacs/diff-mode-"
 
 end

Reply via email to