aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPascal Terjan <pterjan@mageia.org>2022-10-25 20:29:46 +0000
committerPascal Terjan <pterjan@mageia.org>2022-11-01 22:23:53 +0000
commit2c86ed6389d741cfe41323937e9e93b89935490e (patch)
tree046cb4f3b1bf2046d8555ae18100280c3a3fb62c
parent775e1ad51b6318b1a308be9b997accc470329282 (diff)
downloadperl-URPM-2c86ed6389d741cfe41323937e9e93b89935490e.tar
perl-URPM-2c86ed6389d741cfe41323937e9e93b89935490e.tar.gz
perl-URPM-2c86ed6389d741cfe41323937e9e93b89935490e.tar.bz2
perl-URPM-2c86ed6389d741cfe41323937e9e93b89935490e.tar.xz
perl-URPM-2c86ed6389d741cfe41323937e9e93b89935490e.zip
Fix parsing of properties containing [topic/parse_property
For example, the name extracted from a requirement of "python3.10dist(fonttools[unicode])[>= 4.10]" was sometimes "python3.10dist(fonttools" instead of the expected "python3.10dist(fonttools[unicode])". Code parsing such strings existed in many places, it now exists only in 2 places, a perl version in Resolve.pm and a C version in URPM.xs. Both codes used to handle both "foo >= 0" and "foo[>= 0]" but at least the perl code seems to only call it on provides/conflicts/obsoletes which are always using the second form so the support for it was dropped from the perl version for the sake of simplicity.
-rw-r--r--URPM.xs223
-rw-r--r--URPM/Resolve.pm26
-rw-r--r--t/resolve.t46
-rw-r--r--t/synthesis.t52
4 files changed, 268 insertions, 79 deletions
diff --git a/URPM.xs b/URPM.xs
index 7d48acf..f1e37dd 100644
--- a/URPM.xs
+++ b/URPM.xs
@@ -309,6 +309,73 @@ ranges_overlap(rpmsenseFlags aflags, char *sa, rpmsenseFlags bflags, char *sb) {
}
}
+struct property {
+ char *name;
+ rpmsenseFlags flags;
+ char *evr;
+};
+
+static void free_property(struct property *ps) {
+ free(ps->name);
+ ps->name = NULL;
+ ps->flags = 0;
+ free(ps->evr);
+ ps->evr = NULL;
+}
+
+// This parses things like 'ocamlx(bar)[== 42]' or 'ocaml-bar < 42'
+static int parse_property(char *s, struct property *ps) {
+ int l = strlen(s);
+ char *eon = NULL;
+
+ ps->name = NULL;
+ ps->flags = 0;
+ ps->evr = NULL;
+
+ if (l == 0) return -1;
+
+ if (s[l-1] == ']') {
+ eon = strrchr(s, '[');
+
+ if (eon == NULL || eon == s) {
+ // This is a strange one finishing with ] but without a [ before like
+ // "Provides: foo]" or something without a name before like
+ // "Provides: [foo]"
+ ps->name = strdup(s);
+ ps->evr = strdup("");
+ return 0;
+ }
+ }
+
+ /* Drop "[*]" if present at the end of the name */
+ if (eon-s > 3 && !strncmp(eon-3, "[*]", 3)) {
+ eon = eon-3;
+ }
+
+ if (eon == NULL) {
+ // This does not have [ ] at the end but could be in the form "gcc < 42"
+ eon = s;
+ while (*eon && *eon != ' ' && *eon != '<' && *eon != '>') ++eon;
+ }
+
+ ps->name = strndup(s, eon-s);
+ l = strlen(eon);
+ s = eon;
+ while (*s) {
+ if (*s == ' ' || *s == '[' || *s == '*' || *s == ']');
+ else if (*s == '<') ps->flags |= RPMSENSE_LESS;
+ else if (*s == '>') ps->flags |= RPMSENSE_GREATER;
+ else if (*s == '=') ps->flags |= RPMSENSE_EQUAL;
+ else break;
+ ++s;
+ --l;
+ }
+ ps->evr = strndup(s, l-1);
+ /* fprintf(stderr, "name=\"%s\" evr=\"%s\"\n", ps->name, ps->evr); */
+
+ return 0;
+}
+
typedef int (*callback_list_str)(char *s, int slen, const char *name, const rpmsenseFlags flags, const char *evr, void *param);
static int
@@ -339,9 +406,8 @@ callback_list_str_overlap(char *s, int slen, const char *name, rpmsenseFlags fla
struct cb_overlap_s *os = (struct cb_overlap_s *)param;
int result = 0;
char *eos = NULL;
- char *eon = NULL;
char eosc = '\0';
- char eonc = '\0';
+ struct property ops;
/* we need to extract name, flags and evr from a full sense information, store result in local copy */
if (s) {
@@ -350,28 +416,16 @@ callback_list_str_overlap(char *s, int slen, const char *name, rpmsenseFlags fla
eosc = *eos;
*eos = 0;
}
- name = s;
- while (*s && *s != ' ' && *s != '[' && *s != '<' && *s != '>' && *s != '=') ++s;
- if (*s) {
- eon = s;
- while (*s) {
- if (*s == ' ' || *s == '[' || *s == '*' || *s == ']');
- else if (*s == '<') flags |= RPMSENSE_LESS;
- else if (*s == '>') flags |= RPMSENSE_GREATER;
- else if (*s == '=') flags |= RPMSENSE_EQUAL;
- else break;
- ++s;
- }
- evr = s;
- } else
+ if (!parse_property(s, &ops)) {
+ name = ops.name;
+ flags = ops.flags;
+ evr = ops.evr;
+ } else {
+ name = s;
evr = "";
+ }
}
- /* mark end of name */
- if (eon) {
- eonc = *eon;
- *eon = 0;
- }
/* names should be equal, else it will not overlap */
if (!strcmp(name, os->name)) {
/* perform overlap according to direction needed, negative for left */
@@ -384,9 +438,12 @@ callback_list_str_overlap(char *s, int slen, const char *name, rpmsenseFlags fla
/* fprintf(stderr, "cb_list_str_overlap result=%d, os->direction=%d, os->name=%s, os->evr=%s, name=%s, evr=%s\n",
result, os->direction, os->name, os->evr, name, evr); */
- /* restore s if needed */
- if (eon) *eon = eonc;
- if (eos) *eos = eosc;
+ if (s) {
+ free_property(&ops);
+
+ /* restore s if needed */
+ if (eos) *eos = eosc;
+ }
return result;
}
@@ -408,21 +465,29 @@ return_list_str(char *s, const Header header, rpmTag tag_name, rpmTag tag_flags,
if (f(s, 0, NULL, 0, NULL, param))
return -count;
} else {
- char *eos;
+ struct property ops;
while(ps != NULL) {
- *ps = 0; eos = strchr(s, '['); if (!eos) eos = strchr(s, ' ');
- ++count;
- if (f(s, eos ? eos-s : ps-s, NULL, 0, NULL, param)) {
- *ps = '@';
- return -count;
+ *ps = 0;
+ if (!parse_property(s, &ops)) {
+ ++count;
+ if (f(ops.name, strlen(ops.name), NULL, 0, NULL, param)) {
+ *ps = '@';
+ free_property(&ops);
+ return -count;
+ }
+ free_property(&ops);
}
- *ps = '@'; /* restore in memory modified char */
- s = ps + 1; ps = strchr(s, '@');
+ *ps = '@'; /* restore in memory modified char */
+ s = ps + 1; ps = strchr(s, '@');
}
- eos = strchr(s, '['); if (!eos) eos = strchr(s, ' ');
++count;
- if (f(s, eos ? eos-s : 0, NULL, 0, NULL, param))
- return -count;
+ if (!parse_property(s, &ops)) {
+ if (f(ops.name, strlen(ops.name), NULL, 0, NULL, param)) {
+ free_property(&ops);
+ return -count;
+ }
+ free_property(&ops);
+ }
}
} else if (header) {
struct rpmtd_s list, flags, list_evr;
@@ -879,6 +944,7 @@ update_provides(const URPM__Package pkg, HV *provides) {
}
} else {
char *ps, *s, *es;
+ struct property ops;
if ((s = pkg->requires) != NULL && *s != 0) {
ps = strchr(s, '@');
@@ -900,12 +966,24 @@ update_provides(const URPM__Package pkg, HV *provides) {
if ((s = pkg->provides) != NULL && *s != 0) {
ps = strchr(s, '@');
while(ps != NULL) {
- *ps = 0; es = strchr(s, '['); if (!es) es = strchr(s, ' '); *ps = '@';
- update_hash_entry(provides, s, es != NULL ? es-s : ps-s, 1, es != NULL, pkg);
+ *ps = 0;
+ if (parse_property(s, &ops)) {
+ /* Failed to parse, use the whole string */
+ update_hash_entry(provides, s, ps-s, 1, 0, pkg);
+ } else {
+ update_hash_entry(provides, s, strlen(ops.name), 1, ops.flags != 0, pkg);
+ free_property(&ops);
+ }
+ *ps = '@';
s = ps + 1; ps = strchr(s, '@');
}
- es = strchr(s, '['); if (!es) es = strchr(s, ' ');
- update_hash_entry(provides, s, es != NULL ? es-s : 0, 1, es != NULL, pkg);
+ if (parse_property(s, &ops)) {
+ /* Failed to parse, use the whole string */
+ update_hash_entry(provides, s, ps-s, 1, 0, pkg);
+ } else {
+ update_hash_entry(provides, s, strlen(ops.name), 1, ops.flags != 0, pkg);
+ free_property(&ops);
+ }
}
}
}
@@ -925,16 +1003,28 @@ update_obsoletes(const URPM__Package pkg, HV *obsoletes) {
char *ps, *s;
if ((s = pkg->obsoletes) != NULL && *s != 0) {
- char *es;
+ struct property ops;
ps = strchr(s, '@');
while(ps != NULL) {
- *ps = 0; es = strchr(s, '['); if (!es) es = strchr(s, ' '); *ps = '@';
- update_hash_entry(obsoletes, s, es != NULL ? es-s : ps-s, 1, 0, pkg);
+ *ps = 0;
+ if (parse_property(s, &ops)) {
+ /* Failed to parse, use the whole string */
+ update_hash_entry(obsoletes, s, ps-s, 1, 0, pkg);
+ } else {
+ update_hash_entry(obsoletes, s, strlen(ops.name), 1, 0, pkg);
+ free_property(&ops);
+ }
+ *ps = '@';
s = ps + 1; ps = strchr(s, '@');
}
- es = strchr(s, '['); if (!es) es = strchr(s, ' ');
- update_hash_entry(obsoletes, s, es != NULL ? es-s : 0, 1, 0, pkg);
+ if (parse_property(s, &ops)) {
+ /* Failed to parse, use the whole string */
+ update_hash_entry(obsoletes, s, strlen(s), 1, 0, pkg);
+ } else {
+ update_hash_entry(obsoletes, s, ops.flags ? strlen(ops.name) : 0, 1, 0, pkg);
+ free_property(&ops);
+ }
}
}
}
@@ -1507,7 +1597,6 @@ static int get_e_v_r(URPM__Package pkg, int *epoch, char **version, char **relea
return 0;
}
-
MODULE = URPM PACKAGE = URPM::Package PREFIX = Pkg_
void
@@ -1991,8 +2080,7 @@ Pkg_obsoletes_overlap(pkg, s)
provides_overlap = 1
PREINIT:
struct cb_overlap_s os;
- char *eon = NULL;
- char eonc = '\0';
+ struct property ps;
rpmTag tag_name;
rpmTag tag_flags, tag_version;
CODE:
@@ -2008,33 +2096,20 @@ Pkg_obsoletes_overlap(pkg, s)
tag_version = RPMTAG_OBSOLETEVERSION;
break;
}
- os.name = s;
- os.flags = 0;
- while (*s && *s != ' ' && *s != '[' && *s != '<' && *s != '>' && *s != '=') ++s;
- if (*s) {
- eon = s;
- while (*s) {
- if (*s == ' ' || *s == '[' || *s == '*' || *s == ']');
- else if (*s == '<') os.flags |= RPMSENSE_LESS;
- else if (*s == '>') os.flags |= RPMSENSE_GREATER;
- else if (*s == '=') os.flags |= RPMSENSE_EQUAL;
- else break;
- ++s;
- }
- os.evr = s;
- } else
- os.evr = "";
- os.direction = ix == 0 ? -1 : 1;
- /* mark end of name */
- if (eon) {
- eonc = *eon;
- *eon = 0;
- }
- /* return_list_str returns a negative value is the callback has returned non-zero */
- RETVAL = return_list_str(ix == 0 ? pkg->obsoletes : pkg->provides, pkg->h, tag_name, tag_flags, tag_version,
- callback_list_str_overlap, &os) < 0;
- /* restore end of name */
- if (eon) *eon = eonc;
+ if (parse_property(s, &ps)) {
+ fprintf(stderr, "provides_overlap: failed to parse property %s\n", s);
+ RETVAL = -1;
+ } else {
+ os.name = ps.name;
+ os.flags = ps.flags;
+ os.evr = ps.evr;
+ os.direction = ix == 0 ? -1 : 1;
+ fprintf(stderr, "provides_overlap: property=\"%s\" name=\"%s\" evr=\"%s\"\n", s, os.name, os.evr);
+ /* return_list_str returns a negative value is the callback has returned non-zero */
+ RETVAL = return_list_str(ix == 0 ? pkg->obsoletes : pkg->provides, pkg->h, tag_name, tag_flags, tag_version,
+ callback_list_str_overlap, &os) < 0;
+ free_property(&ps);
+ }
OUTPUT:
RETVAL
diff --git a/URPM/Resolve.pm b/URPM/Resolve.pm
index dded564..1816eb1 100644
--- a/URPM/Resolve.pm
+++ b/URPM/Resolve.pm
@@ -58,7 +58,12 @@ Returns the property name (eg: "mageia-release" in above example)
sub property2name {
my ($property) = @_;
- $property =~ /^([^\s\[]*)/ && $1;
+ my $name = $property;
+ if ($property =~ /^([^\s]+)\s*\[[^\[]*\]$/) {
+ $name = $1;
+ }
+ $name =~ s/\[\*\]$//;
+ return $name;
}
=item property2name_range($property)
@@ -69,7 +74,14 @@ Returns the property name & range (eg: "mageia-release" & ">= 1" in above exampl
sub property2name_range {
my ($property) = @_;
- $property =~ /^([^\s\[]*)(?:\[\*\])?\[?([^\s\]]*\s*[^\s\]]*)/;
+ my ($name, $range) = ($property, "");
+ $name =~ s/\[\*\]$//;
+ if ($name =~ /^([^\s]+)\s*\[([^\[]*)\]$/) {
+ $name = $1;
+ $range = $2;
+ }
+ $name =~ s/\[\*\]$//;
+ return ($name, $range);
}
=item property2name_op_version($property)
@@ -81,7 +93,15 @@ Returns the property name, operator & range (eg: "mageia-release", ">=", & "1" i
sub property2name_op_version {
my ($property) = @_;
- $property =~ /^([^\s\[]*)(?:\[\*\])?\s*\[?([^\s\]]*)\s*([^\s\]]*)/;
+ my ($name, $op, $version) = ($property, "", "");
+ $name =~ s/\[\*\]$//;
+ if ($name =~ /^([^\s]+)\s*\[([^\s\[]*)\s*([^\s\]]*)\]$/) {
+ $name = $1;
+ $op = $2;
+ $version = $3;
+ }
+ $name =~ s/\[\*\]$//;
+ return ($name, $op, $version);
}
diff --git a/t/resolve.t b/t/resolve.t
new file mode 100644
index 0000000..56869b1
--- /dev/null
+++ b/t/resolve.t
@@ -0,0 +1,46 @@
+#!/usr/bin/perl
+
+use strict ;
+use warnings ;
+use Test::More;
+use URPM::Resolve;
+
+my $testcases = [
+ {
+ property => 'mageia-release[>= 1]',
+ name => 'mageia-release',
+ op => '>=',
+ version => '1',
+ }, {
+ property => 'python3.10dist(fonttools[unicode])[>= 4.10]',
+ name => 'python3.10dist(fonttools[unicode])',
+ op => '>=',
+ version => '4.10',
+ }, {
+ property => 'python3.10dist(fonttools[unicode])',
+ name => 'python3.10dist(fonttools[unicode])',
+ op => '',
+ version => '',
+ }, {
+ property => 'openssl[*][>= 0.9.7]',
+ name => 'openssl',
+ op => '>=',
+ version => '0.9.7',
+ }, {
+ property => 'openssl[*]',
+ name => 'openssl',
+ op => '',
+ version => '',
+ }];
+
+foreach my $tc (@$testcases) {
+ is(URPM::property2name($tc->{property}), $tc->{name}, "property2name(\"$tc->{property}\")");
+ my $expected = [$tc->{name}, $tc->{op} ? "$tc->{op} $tc->{version}" : ""];
+ my @got = URPM::property2name_range($tc->{property});
+ is_deeply(\@got, $expected, "property2name_range(\"$tc->{property}\")");
+ $expected = [$tc->{name}, $tc->{op}, $tc->{version}];
+ @got = URPM::property2name_op_version($tc->{property});
+ is_deeply(\@got, $expected, "property2name_op_version(\"$tc->{property}\")");
+}
+
+done_testing();
diff --git a/t/synthesis.t b/t/synthesis.t
index 8246b36..794fd7a 100644
--- a/t/synthesis.t
+++ b/t/synthesis.t
@@ -2,7 +2,7 @@
use strict ;
use warnings ;
-use Test::More tests => 95;
+use Test::More tests => 113;
use URPM;
chdir 't' if -d 't';
@@ -27,7 +27,25 @@ $s =~ s/-devel//g;
print $f $s;
close $f;
-END { unlink $file1, $file2 }
+$s = <<'EOF';
+@provides@python3-fonttools+ufo[== 4.18.2-4.mga9]@python3dist(fonttools[ufo])[== 4.18.2]
+@summary@Metapackage for python3-fonttools: ufo extras
+@info@python3-fonttools+ufo-4.18.2-4.mga9.noarch@0@221683@Development/Other
+@provides@config(dovecot)[== 2.3.19.1-2.mga9]
+@requires@openssl[*]@rpm-helper[*][>= 0.21]
+@summary@Secure IMAP and POP3 server
+@info@dovecot-2.3.19.1-2.mga9.x86_64@0@17815993@System/Servers
+@provides@openimageio[== 2.3.20.0-2.mga9]@font(:lang=aa)
+@summary@Library for reading and writing images
+@info@openimageio-2.3.20.0-2.mga9.x86_64@0@42@Development/Other
+EOF
+
+my $file3 = 'synthesis.squarebrackets.cz';
+open $f, "| gzip -9 >$file3";
+print $f $s;
+close $f;
+
+END { unlink $file1, $file2, $file3 }
my $a = URPM->new;
ok($a);
@@ -161,3 +179,33 @@ ok($requires[8] eq 'rpmlib(CompressedFileNames)');
@provides = $pkg->provides_nosense;
ok(@provides == 1);
ok($provides[0] eq 'glibc-devel');
+
+my $b = URPM->new;
+$b->parse_synthesis($file3);
+$pkg = $b->{depslist}[0];
+ok($pkg);
+is($pkg->name, 'python3-fonttools+ufo');
+@provides = $pkg->provides;
+is(@provides, 2);
+is($provides[1], 'python3dist(fonttools[ufo])[== 4.18.2]');
+@provides = $pkg->provides_nosense;
+is(@provides, 2);
+is($provides[1], 'python3dist(fonttools[ufo])');
+$pkg = $b->{depslist}[1];
+ok($pkg);
+is($pkg->name, 'dovecot');
+@requires = $pkg->requires;
+is(@requires, 2);
+is($requires[0], 'openssl[*]');
+is($requires[1], 'rpm-helper[*][>= 0.21]');
+@requires = $pkg->requires_nosense;
+is(@requires, 2);
+is($requires[0], 'openssl');
+is($requires[1], 'rpm-helper');
+$pkg = $b->{depslist}[2];
+ok($pkg);
+is($pkg->name, 'openimageio');
+@provides = $pkg->provides;
+is(@provides, 2);
+is($provides[1], 'font(:lang=aa)');
+