1# ex:ts=8 sw=4:
2# $OpenBSD: RequiredBy.pm,v 1.30 2023/06/13 09:07:17 espie Exp $
3#
4# Copyright (c) 2003-2005 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 v5.36;
19
20package OpenBSD::RequirementList;
21use OpenBSD::PackageInfo;
22use Carp;
23
24sub fatal_error($self, $msg)
25{
26	require OpenBSD::Tracker;
27	OpenBSD::Tracker->dump;
28	confess ref($self), ": $msg $self->{filename}: $!";
29}
30
31sub fill_entries($self)
32{
33	if (!exists $self->{entries}) {
34		my $l = $self->{entries} = {};
35
36		if (-f $self->{filename}) {
37			open(my $fh, '<', $self->{filename}) or
38			    $self->fatal_error("reading");
39			while(<$fh>) {
40				s/\s+$//o;
41				next if /^$/o;
42				chomp;
43				$l->{$_} = 1;
44			}
45			close($fh);
46			$self->{nonempty} = 1;
47		} else {
48			$self->{nonempty} = 0;
49		}
50	}
51}
52
53sub synch($self)
54{
55	return $self if $main::not;
56
57	if (!unlink $self->{filename}) {
58		if ($self->{nonempty}) {
59		    croak ref($self), ": erasing $self->{filename}: $!";
60		}
61	}
62	if (%{$self->{entries}}) {
63		open(my $fh, '>', $self->{filename}) or
64		    $self->fatal_error("writing");
65		while (my ($k, $v) = each %{$self->{entries}}) {
66			print $fh "$k\n";
67		}
68		close($fh) or
69		    croak ref($self), ": closing $self->{filename}: $!";
70		$self->{nonempty} = 1;
71	} else {
72		$self->{nonempty} = 0;
73	}
74	return $self;
75}
76
77sub list($self)
78{
79	if (wantarray) {
80		$self->fill_entries;
81		return keys %{$self->{entries}};
82	} else {
83		if (exists $self->{entries}) {
84			return %{$self->{entries}} ? 1 : 0;
85		} elsif (!exists $self->{nonempty}) {
86			$self->{nonempty} = -f $self->{filename} ? 1 : 0;
87		}
88		return $self->{nonempty};
89	}
90}
91
92sub erase($self)
93{
94	$self->{entries} = {};
95	$self->synch;
96}
97
98sub delete($self, @pkgnames)
99{
100	$self->fill_entries;
101	for my $pkg (@pkgnames) {
102		delete $self->{entries}->{$pkg};
103	}
104	$self->synch;
105}
106
107sub add($self, @pkgnames)
108{
109	$self->fill_entries;
110	for my $pkg (@pkgnames) {
111		$self->{entries}->{$pkg} = 1;
112	}
113	$self->synch;
114}
115
116my $cache = {};
117
118sub new($class, $pkgname)
119{
120	my $f = installed_info($pkgname).$class->filename;
121	if (!exists $cache->{$f}) {
122		return $cache->{$f} = bless { filename => $f }, $class;
123	}
124	return $cache->{$f};
125}
126
127sub forget($class, $dir)
128{
129	my $f = $dir.$class->filename;
130	if (exists $cache->{$f}) {
131		$cache->{$f}->{entries} = {};
132		$cache->{$f}->{nonempty} = 0;
133	}
134}
135
136sub compute_closure($class, @seed)
137{
138	my @todo = @seed;
139	my %done = ();
140
141	while (my $pkgname = pop @todo) {
142		next if $done{$pkgname};
143		$done{$pkgname} = 1;
144		for my $dep ($class->new($pkgname)->list) {
145			next if defined $done{$dep};
146			push(@todo, $dep);
147		}
148	}
149	return keys %done;
150}
151
152package OpenBSD::RequiredBy;
153our @ISA=qw(OpenBSD::RequirementList);
154use OpenBSD::PackageInfo;
155
156sub filename($) { REQUIRED_BY };
157
158package OpenBSD::Requiring;
159our @ISA=qw(OpenBSD::RequirementList);
160use OpenBSD::PackageInfo;
161
162sub filename($) { REQUIRING };
163
1641;
165