1# ex:ts=8 sw=4:
2# $OpenBSD: Signature.pm,v 1.24 2019/05/08 13:04:27 espie Exp $
3#
4# Copyright (c) 2010 Marc Espie <espie@openbsd.org>
5#
6# Permission to use, copy, modify, and distribute this software for any
7# purpose with or without fee is hereby granted, provided that the above
8# copyright notice and this permission notice appear in all copies.
9#
10# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
18use strict;
19use warnings;
20
21package OpenBSD::PackingElement;
22sub signature {}
23
24package OpenBSD::PackingElement::VersionElement;
25sub signature
26{
27	my ($self, $hash) = @_;
28	$hash->{$self->signature_key} = $self;
29}
30
31sub always
32{
33	return 1;
34}
35
36package OpenBSD::PackingElement::Version;
37sub signature
38{
39	&OpenBSD::PackingElement::VersionElement::signature;
40}
41
42package OpenBSD::PackingElement::Dependency;
43sub signature_key
44{
45	my $self = shift;
46	return $self->{pkgpath};
47}
48
49sub sigspec
50{
51	my $self = shift;
52	return OpenBSD::PackageName->from_string($self->{def});
53}
54
55sub long_string
56{
57	my $self = shift;
58	return '@'.$self->sigspec->to_string;
59}
60
61sub compare
62{
63	my ($a, $b) = @_;
64	return $a->sigspec->compare($b->sigspec);
65}
66
67sub always
68{
69	return 0;
70}
71
72package OpenBSD::PackingElement::Wantlib;
73sub signature_key
74{
75	my $self = shift;
76	my $spec = $self->spec;
77	if ($spec->is_valid) {
78		return $spec->key;
79	} else {
80		return "???";
81	}
82}
83
84sub compare
85{
86	my ($a, $b) = @_;
87	return $a->spec->compare($b->spec);
88}
89
90sub long_string
91{
92	my $self = shift;
93	return $self->spec->to_string;
94}
95
96sub always
97{
98	return 1;
99}
100
101package OpenBSD::PackingElement::Version;
102sub signature_key
103{
104	return 'VERSION';
105}
106
107sub long_string
108{
109	my $self = shift;
110	return $self->{name};
111}
112
113sub compare
114{
115	my ($a, $b) = @_;
116	return $a->{name} <=> $b->{name};
117}
118
119package OpenBSD::Signature;
120sub from_plist
121{
122	my ($class, $plist) = @_;
123
124	my $k = {};
125	$plist->visit('signature', $k);
126
127	$k->{VERSION} //= OpenBSD::PackingElement::Version->new(0);
128
129	if ($plist->has('always-update')) {
130		return $class->full->new($plist->pkgname, $k, $plist);
131	} else {
132		return $class->new($plist->pkgname, $k);
133	}
134}
135
136sub full
137{
138	return "OpenBSD::Signature::Full";
139}
140
141sub new
142{
143	my ($class, $pkgname, $extra) = @_;
144	bless { name => $pkgname, extra => $extra }, $class;
145}
146
147sub string
148{
149	my $self = shift;
150	return join(',', $self->{name}, sort map {$_->long_string} values %{$self->{extra}});
151}
152
153sub compare
154{
155	my ($a, $b, $state) = @_;
156	return $b->revert_compare($a, $state);
157}
158
159sub revert_compare
160{
161	my ($b, $a, $state) = @_;
162
163
164	if ($a->{name} eq $b->{name}) {
165		# first check if system version changed
166		# then we don't have to go any further
167		my $d = $b->{extra}{VERSION}->name - $a->{extra}{VERSION}->name;
168		if ($d < 0) {
169			return 1;
170		} elsif ($d > 0) {
171			return -1;
172		}
173
174		my $shortened = $state->defines("SHORTENED");
175		my $awins = 0;
176		my $bwins = 0;
177		my $done = {};
178		my $errors = 0;
179		while (my ($k, $v) = each %{$a->{extra}}) {
180			if (!defined $b->{extra}{$k}) {
181				$state->errsay(
182				    "Couldn't find #1 in second signature", $k);
183				$errors++;
184				next;
185			}
186			$done->{$k} = 1;
187			next if $shortened && !$v->always;
188			my $r = $v->compare($b->{extra}{$k});
189			if ($r > 0) {
190				$awins++;
191			} elsif ($r < 0) {
192				$bwins++;
193			}
194		}
195		for my $k (keys %{$b->{extra}}) {
196			if (!$done->{$k}) {
197				$state->errsay(
198				    "Couldn't find #1 in first signature", $k);
199				$errors++;
200			}
201		}
202		if ($errors) {
203			$a->print_error($b, $state);
204			return undef;
205		}
206		if ($awins == 0) {
207			return -$bwins;
208		} elsif ($bwins == 0) {
209			return $awins;
210		} else {
211			return undef;
212		}
213	} else {
214		return OpenBSD::PackageName->from_string($a->{name})->compare(OpenBSD::PackageName->from_string($b->{name}));
215	}
216}
217
218sub print_error
219{
220	my ($a, $b, $state) = @_;
221
222	$state->errsay("Error: #1 exists in two non-comparable versions",
223	    $a->{name});
224	$state->errsay("Someone forgot to bump a REVISION");
225	$state->errsay("#1 vs. #2", $a->string, $b->string);
226}
227
228package OpenBSD::Signature::Full;
229our @ISA=qw(OpenBSD::Signature);
230
231sub new
232{
233	my ($class, $pkgname, $extra, $plist) = @_;
234	my $o = $class->SUPER::new($pkgname, $extra);
235	my $hash;
236	open my $fh, '>', \$hash;
237	$plist->write_without_variation($fh);
238	close $fh;
239	$o->{hash} = $hash;
240	return $o;
241}
242
243sub string
244{
245	my $self = shift;
246	return join(',', $self->SUPER::string, $self->{hash});
247}
248
249sub revert_compare
250{
251	my ($b, $a, $state) = @_;
252	my $r = $b->SUPER::revert_compare($a, $state);
253	if (defined $r && $r == 0) {
254		if ($a->string ne $b->string) {
255			return undef;
256		}
257	}
258	return $r;
259}
260
261sub compare
262{
263	my ($a, $b, $state) = @_;
264	my $r = $a->SUPER::compare($b, $state);
265	if (defined $r && $r == 0) {
266		if ($a->string ne $b->string) {
267			return undef;
268		}
269	}
270	return $r;
271}
272
2731;
274