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