xref: /openbsd-src/usr.sbin/pkg_add/OpenBSD/Add.pm (revision d6425245cb6e8bb18968b87edab8e071f7534ef0)
18968f6aaSespie# ex:ts=8 sw=4:
2*d6425245Sespie# $OpenBSD: Add.pm,v 1.196 2023/10/11 13:54:43 espie Exp $
38968f6aaSespie#
445019a4aSespie# Copyright (c) 2003-2014 Marc Espie <espie@openbsd.org>
58968f6aaSespie#
68968f6aaSespie# Permission to use, copy, modify, and distribute this software for any
78968f6aaSespie# purpose with or without fee is hereby granted, provided that the above
88968f6aaSespie# copyright notice and this permission notice appear in all copies.
98968f6aaSespie#
108968f6aaSespie# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
118968f6aaSespie# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
128968f6aaSespie# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
138968f6aaSespie# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
148968f6aaSespie# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
158968f6aaSespie# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
168968f6aaSespie# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
170fbefeddSespie
18039cbdaaSespieuse v5.36;
198968f6aaSespie
208968f6aaSespiepackage OpenBSD::Add;
219e4de79fSespieuse OpenBSD::Error;
22ebc3e79eSespieuse OpenBSD::PackageInfo;
23cddc17abSespieuse OpenBSD::ArcCheck;
2473c16894Sespieuse OpenBSD::Paths;
257f105815Sespieuse File::Copy;
268968f6aaSespie
27039cbdaaSespiesub manpages_index($state)
289e4de79fSespie{
29005e7d56Sespie	return unless defined $state->{addman};
309e4de79fSespie	my $destdir = $state->{destdir};
319e4de79fSespie
325f30bd74Sespie	# fudge verbose for API differences
33005e7d56Sespie	while (my ($k, $v) = each %{$state->{addman}}) {
34005e7d56Sespie		my @l = map { "$destdir$k/$_" } @$v;
359e4de79fSespie		if ($state->{not}) {
367e83eca3Sespie			$state->say("Merging manpages in #1: #2",
375f30bd74Sespie			    $destdir.$k, join(' ', @l)) if $state->verbose;
389e4de79fSespie		} else {
39101180a9Sespie			$state->run_makewhatis(['-d', $destdir.$k], \@l);
409e4de79fSespie		}
419e4de79fSespie	}
42005e7d56Sespie	delete $state->{addman};
439e4de79fSespie}
449e4de79fSespie
45039cbdaaSespiesub register_installation($plist, $state)
469e4de79fSespie{
4709ca648fSespie	if ($state->{not}) {
4809ca648fSespie		$plist->to_cache;
4909ca648fSespie	} else {
50c2569c13Sespie		my $dest = installed_info($plist->pkgname);
519e4de79fSespie		mkdir($dest);
528d5333baSespie		$plist->copy_info($dest, $state);
53c2569c13Sespie		$plist->set_infodir($dest);
54f04afe28Sespie		$plist->to_installation;
559e4de79fSespie	}
5609ca648fSespie}
579e4de79fSespie
58039cbdaaSespiesub validate_plist($plist, $state, $set)
599e4de79fSespie{
60062c25acSespie	$plist->prepare_for_addition($state, $plist->pkgname, $set);
619e4de79fSespie}
62ebc3e79eSespie
63039cbdaaSespiesub record_partial_installation($plist, $state, $h)
64c767db06Sespie{
65c767db06Sespie	use OpenBSD::PackingElement;
66c767db06Sespie
676a5fab34Sespie	my $n = $plist->make_shallow_copy($h);
68f04afe28Sespie	my $borked = borked_package($plist->pkgname);
693f7a0022Sespie	$n->set_pkgname($borked);
70c767db06Sespie
71c767db06Sespie	# last file may have not copied correctly
725c5127c1Sespie	my $last = $n->{state}{lastfile};
73700d29cbSespie	if (defined $last && defined($last->{d})) {
74c767db06Sespie
75700d29cbSespie		my $old = $last->{d};
761f6201e6Sespie		my $lastname = $last->realname($state);
778cf6695bSespie		if (-f $lastname) {
78700d29cbSespie			$last->{d} = $last->compute_digest($lastname, $old);
79700d29cbSespie			if (!$old->equals($last->{d})) {
807e83eca3Sespie				$state->say("Adjusting #1 for #2 from #3 to #4",
817e83eca3Sespie				    $old->keyword, $lastname, $old->stringize,
827e83eca3Sespie				    $last->{d}->stringize);
83c767db06Sespie			}
848cf6695bSespie		} else {
8595a49439Sespie			delete $last->{d};
868cf6695bSespie		}
87c767db06Sespie	}
886428b005Sespie	register_installation($n, $state);
893f2ef483Sespie	return $borked;
903f2ef483Sespie}
913f2ef483Sespie
92039cbdaaSespiesub perform_installation($handle, $state)
939a36c239Sespie{
94*d6425245Sespie	return if $state->{regression}{stub} && $handle->pkgname !~ /^quirks\-/;
950c61d0f0Sespie
9695a49439Sespie	$state->{partial} = $handle->{partial};
9723b5e034Sespie	$state->progress->visit_with_size($handle->{plist}, 'install');
98cdd4712cSespie	if ($handle->{location}{early_close}) {
99cdd4712cSespie		$handle->{location}->close_now;
100cdd4712cSespie	} else {
1019a36c239Sespie		$handle->{location}->finish_and_close;
1029a36c239Sespie	}
103cdd4712cSespie}
1049a36c239Sespie
105039cbdaaSespiesub skip_to_the_end($handle, $state, $tied, $p)
1062eca3cddSespie{
1078faa7e80Sespie	$state->tweak_header("skipping");
108cdd4712cSespie	for my $e (values %$tied) {
109cdd4712cSespie		$e->tie($state);
110d4147c99Sespie		$p->advance($e);
111cdd4712cSespie	}
112cdd4712cSespie	if (keys %$tied > 0) {
11314093141Sespie		# skipped entries should still be read in CACHE mode
11414093141Sespie		if (defined $state->cache_directory) {
11514093141Sespie			while (my $e = $state->{archive}->next) {
11614093141Sespie			}
11714093141Sespie		} else {
118cdd4712cSespie			$handle->{location}{early_close} = 1;
119cdd4712cSespie		}
12014093141Sespie	}
121ffa43a13Sespie}
122ffa43a13Sespie
123039cbdaaSespiesub perform_extraction($handle, $state)
124ffa43a13Sespie{
125*d6425245Sespie	return if $state->{regression}{stub} && $handle->pkgname !~ /^quirks\-/;
126ffa43a13Sespie
127ffa43a13Sespie	$handle->{partial} = {};
128ffa43a13Sespie	$state->{partial} = $handle->{partial};
129ffa43a13Sespie	$state->{archive} = $handle->{location};
130ffa43a13Sespie	$state->{check_digest} = $handle->{plist}{check_digest};
131ffa43a13Sespie
132ffa43a13Sespie	# archives are actually stored out of order, find_extractible
133ffa43a13Sespie	# will dispatch the packing-list  entries into hashes keyed by names.
134ffa43a13Sespie	# For "tied" entries, also see tie_files in OpenBSD::PkgAdd.
135ffa43a13Sespie	my ($wanted, $tied) = ({}, {});
136ffa43a13Sespie	$handle->{plist}->find_extractible($state, $wanted, $tied);
137c3b72fabSespie	my $p = $state->progress->new_sizer($handle->{plist});
138ffa43a13Sespie
139ffa43a13Sespie	# so iterate over the archive, and "consume" hashes entry as we go
140ffa43a13Sespie	# it's necessary to delete them so that skip_to_the_end will work
141ffa43a13Sespie	# correctly (relies on wanted being empty to trigger, and requires
142ffa43a13Sespie	# tied to be correct for the progress meter).
143ffa43a13Sespie	if (keys %$wanted == 0) {
144ffa43a13Sespie		skip_to_the_end($handle, $state, $tied, $p);
14588e129ceSespie		return;
146cdd4712cSespie	}
14788e129ceSespie	while (my $file = $state->{archive}->next) {
148cdd4712cSespie		my $e = $tied->{$file->name};
149cdd4712cSespie		if (defined $e) {
150cdd4712cSespie			delete $tied->{$file->name};
151cdd4712cSespie			$e->prepare_to_extract($state, $file);
152cdd4712cSespie			$e->tie($state);
153cdd4712cSespie			$state->{archive}->skip;
154cdd4712cSespie			$p->advance($e);
155cdd4712cSespie			# skip to next;
156cdd4712cSespie			next;
157cdd4712cSespie		}
158cdd4712cSespie		$e = $wanted->{$file->name};
159cdd4712cSespie		if (!defined $e) {
160cdd4712cSespie			$state->fatal("archive member not found #1",
161cdd4712cSespie			    $file->name);
162cdd4712cSespie		}
163cdd4712cSespie		delete $wanted->{$file->name};
164ffa43a13Sespie		# note that readmes are only recorded when !tied, since
165ffa43a13Sespie		# we only care if they changed
1662cac224bSespie		my $fullname = $e->fullname;
1672cac224bSespie		if ($fullname =~ m,^$state->{localbase}/share/doc/pkg-readmes/,) {
1682cac224bSespie			push(@{$state->{readmes}}, $fullname);
1692cac224bSespie		}
1702cac224bSespie
171cdd4712cSespie		$e->prepare_to_extract($state, $file);
172cdd4712cSespie		$e->extract($state, $file);
173cdd4712cSespie		$p->advance($e);
17488e129ceSespie		if (keys %$wanted == 0) {
17588e129ceSespie			skip_to_the_end($handle, $state, $tied, $p);
17688e129ceSespie			last;
17788e129ceSespie		}
178cdd4712cSespie	}
179cdd4712cSespie	if (keys %$wanted > 0) {
180cdd4712cSespie		$state->fatal("Truncated archive");
181cdd4712cSespie	}
1822eca3cddSespie}
1832eca3cddSespie
1842735aeecSespiemy $user_tagged = {};
1852735aeecSespie
186039cbdaaSespiesub extract_pkgname($pkgname)
1872735aeecSespie{
1882735aeecSespie	$pkgname =~ s/^.*\///;
1892735aeecSespie	$pkgname =~ s/\.tgz$//;
1902735aeecSespie	return $pkgname;
1912735aeecSespie}
1922735aeecSespie
193039cbdaaSespiesub tweak_package_status($pkgname, $state)
1942735aeecSespie{
1952735aeecSespie	$pkgname = extract_pkgname($pkgname);
1962735aeecSespie	return 0 unless is_installed($pkgname);
1972735aeecSespie	return 0 unless $user_tagged->{$pkgname};
19864d38bd3Sespie	return 1 if $state->{not};
1992735aeecSespie	my $plist = OpenBSD::PackingList->from_installation($pkgname);
20031a74889Sespie	if ($plist->has('manual-installation') && $state->{automatic} > 1) {
2012735aeecSespie		delete $plist->{'manual-installation'};
2022735aeecSespie		$plist->to_installation;
2032735aeecSespie		return 1;
2042735aeecSespie	} elsif (!$plist->has('manual-installation') && !$state->{automatic}) {
2052735aeecSespie		OpenBSD::PackingElement::ManualInstallation->add($plist);
2062735aeecSespie		$plist->to_installation;
2072735aeecSespie		return 1;
2082735aeecSespie	}
2092735aeecSespie	return 0;
2102735aeecSespie}
2112735aeecSespie
212039cbdaaSespiesub tweak_plist_status($plist, $state)
2132735aeecSespie{
2142735aeecSespie	my $pkgname = $plist->pkgname;
2153f7ff4b1Sespie	if ($state->defines('FW_UPDATE')) {
2163f7ff4b1Sespie		$plist->has('firmware') or
2173f7ff4b1Sespie			OpenBSD::PackingElement::Firmware->add($plist);
2183f7ff4b1Sespie	}
2192735aeecSespie	return 0 unless $user_tagged->{$pkgname};
2202735aeecSespie	if (!$plist->has('manual-installation') && !$state->{automatic}) {
2212735aeecSespie		OpenBSD::PackingElement::ManualInstallation->add($plist);
2222735aeecSespie	}
2232735aeecSespie}
2242735aeecSespie
225039cbdaaSespiesub tag_user_packages(@p)
2262735aeecSespie{
227039cbdaaSespie	for my $set (@p) {
228d4d7cbccSespie		for my $n ($set->newer_names) {
229d4d7cbccSespie			$user_tagged->{OpenBSD::PackageName::url2pkgname($n)} = 1;
230fa7d327fSespie		}
2312735aeecSespie	}
2322735aeecSespie}
2332735aeecSespie
234ffa43a13Sespie# The whole package addition/replacecement works like this:
235ffa43a13Sespie# first we run tie_files in PkgAdd to figure out tieto
236ffa43a13Sespie# then "find_extractible" figures out the element of the plist that
237ffa43a13Sespie# belong in the archive (thus find_extractible is the hook that always
238ffa43a13Sespie# gets run on every plist entry just prior to extraction/skipping)
239ffa43a13Sespie#
240ffa43a13Sespie# Then the actual extraction proceeds through "prepare_to_extract" and
241ffa43a13Sespie# either "tie' OR "extract" depending on the element status.
242ffa43a13Sespie# Then later on, we run "install".
243ffa43a13Sespie#
244ffa43a13Sespie# Actual file system entries may get a tempname, or avoid temp altogether
245ffa43a13Sespie#
246ffa43a13Sespie# In case of replacement, tempname will get used if the name is the same
247ffa43a13Sespie# but the file content is different.
248ffa43a13Sespie#
249ffa43a13Sespie# If pkg_add can figure out the name is the same, it will set avoidtemp
250ffa43a13Sespie#
251ffa43a13Sespie# Note that directories, hardlinks and symlinks are purely plist objects
252ffa43a13Sespie# with no archive existence:
253ffa43a13Sespie# Links always get deleted/re-added even in replacement mode, while directory
254ffa43a13Sespie# deletion is delayed into OpenBSD::SharedItems, since several packages
255ffa43a13Sespie# may mention the same directory.
256ffa43a13Sespie#
2578968f6aaSespiepackage OpenBSD::PackingElement;
2588968f6aaSespieuse OpenBSD::Error;
2598968f6aaSespie
260ffa43a13Sespie# used by newuser/newgroup to deal with options.
2618968f6aaSespiemy ($uidcache, $gidcache);
2628968f6aaSespie
2637c22bbb7Sespie# $self->prepare_for_addition($state, $pkgname, $set)
264039cbdaaSespiesub prepare_for_addition($, $, $, $)
2653511fba3Sespie{
2663511fba3Sespie}
2673511fba3Sespie
2687c22bbb7Sespie# $self->find_extractible($state, $wanted, $tied):
2697c22bbb7Sespie#	sort item into wanted (needed from archive) / tied (already there)
270039cbdaaSespiesub find_extractible($, $, $, $)
271cdd4712cSespie{
272cdd4712cSespie}
273cdd4712cSespie
274039cbdaaSespiesub extract($self, $state)
2758968f6aaSespie{
27695a49439Sespie	$state->{partial}{$self} = 1;
27733efc49fSespie	if ($state->{interrupted}) {
27833efc49fSespie		die "Interrupted";
27933efc49fSespie	}
2802eca3cddSespie}
2812eca3cddSespie
282039cbdaaSespiesub install($self, $state)
2832eca3cddSespie{
28495a49439Sespie	# XXX "normal" items are already in partial, but NOT stuff
28595a49439Sespie	# that's install-only, like symlinks and dirs...
28695a49439Sespie	$state->{partial}{$self} = 1;
2872eca3cddSespie	if ($state->{interrupted}) {
2882eca3cddSespie		die "Interrupted";
2892eca3cddSespie	}
2908968f6aaSespie}
2918968f6aaSespie
292039cbdaaSespie# $self->copy_info($dest, $state)
293039cbdaaSespiesub copy_info($, $, $)
294c2569c13Sespie{
295c2569c13Sespie}
296c2569c13Sespie
297039cbdaaSespiesub set_modes($self, $state, $name)
2988968f6aaSespie{
2998968f6aaSespie	if (defined $self->{owner} || defined $self->{group}) {
3008968f6aaSespie		require OpenBSD::IdCache;
3018968f6aaSespie
3028968f6aaSespie		if (!defined $uidcache) {
303f04afe28Sespie			$uidcache = OpenBSD::UidCache->new;
304f04afe28Sespie			$gidcache = OpenBSD::GidCache->new;
3058968f6aaSespie		}
306178c9266Sespie		my ($uid, $gid) = (-1, -1);
3078968f6aaSespie		if (defined $self->{owner}) {
3088968f6aaSespie			$uid = $uidcache->lookup($self->{owner}, $uid);
3098968f6aaSespie		}
3108968f6aaSespie		if (defined $self->{group}) {
3118968f6aaSespie			$gid = $gidcache->lookup($self->{group}, $gid);
3128968f6aaSespie		}
3138968f6aaSespie		chown $uid, $gid, $name;
3148968f6aaSespie	}
3158968f6aaSespie	if (defined $self->{mode}) {
3168968f6aaSespie		my $v = $self->{mode};
317cc24e6f2Sespie		if ($v =~ m/^\d+$/o) {
3188968f6aaSespie			chmod oct($v), $name;
3198968f6aaSespie		} else {
320178c9266Sespie			$state->system(OpenBSD::Paths->chmod,
321178c9266Sespie			    $self->{mode}, $name);
3228968f6aaSespie		}
3238968f6aaSespie	}
32431374c36Sespie	if (defined $self->{ts}) {
32531374c36Sespie		utime $self->{ts}, $self->{ts}, $name;
32631374c36Sespie	}
3278968f6aaSespie}
3288968f6aaSespie
329cdd4712cSespiepackage OpenBSD::PackingElement::Meta;
330cdd4712cSespie
331cdd4712cSespie# XXX stuff that's invisible to find_extractible should be considered extracted
332cdd4712cSespie# for the most part, otherwise we create broken partial packages
333039cbdaaSespiesub find_extractible($self, $state, $wanted, $tied)
334cdd4712cSespie{
335cdd4712cSespie	$state->{partial}{$self} = 1;
336cdd4712cSespie}
337cdd4712cSespie
3385c5127c1Sespiepackage OpenBSD::PackingElement::Cwd;
339039cbdaaSespiesub find_extractible	# forwarder
3405c5127c1Sespie{
3415c5127c1Sespie	&OpenBSD::PackingElement::Meta::find_extractible;
3425c5127c1Sespie}
3435c5127c1Sespie
3443511fba3Sespiepackage OpenBSD::PackingElement::ExtraInfo;
3453511fba3Sespieuse OpenBSD::Error;
3463511fba3Sespie
347039cbdaaSespiesub prepare_for_addition($self, $state, $pkgname, $)
3483511fba3Sespie{
3493511fba3Sespie	if ($state->{ftp_only} && $self->{ftp} ne 'yes') {
3507e83eca3Sespie	    $state->errsay("Package #1 is not for ftp", $pkgname);
35185a4d1f9Sespie	    $state->{problems}++;
3523511fba3Sespie	}
3533511fba3Sespie}
3543511fba3Sespie
3553511fba3Sespiepackage OpenBSD::PackingElement::NewAuth;
3563511fba3Sespieuse OpenBSD::Error;
3573511fba3Sespie
358039cbdaaSespiesub add_entry($, $l, @p)
3598968f6aaSespie{
360039cbdaaSespie	while (@p >= 2) {
361039cbdaaSespie		my $f = shift @p;
362039cbdaaSespie		my $v = shift @p;
3638968f6aaSespie		next if !defined $v or $v eq '';
364a00f6995Sespie		if ($v =~ m/^\!(.*)$/o) {
365a00f6995Sespie			push(@$l, $f, $1);
3668968f6aaSespie		} else {
3678968f6aaSespie			push(@$l, $f, $v);
3688968f6aaSespie		}
3698968f6aaSespie	}
3708968f6aaSespie}
3718968f6aaSespie
372039cbdaaSespiesub prepare_for_addition($self, $state, $pkgname, $)
3733511fba3Sespie{
37485a4d1f9Sespie	my $ok = $self->check;
3753511fba3Sespie	if (defined $ok) {
3763511fba3Sespie		if ($ok == 0) {
3777e83eca3Sespie			$state->errsay("#1 #2 does not match",
3787e83eca3Sespie			    $self->type, $self->name);
37985a4d1f9Sespie			$state->{problems}++;
3803511fba3Sespie		}
3813511fba3Sespie	}
3823511fba3Sespie	$self->{okay} = $ok;
3833511fba3Sespie}
3848968f6aaSespie
385039cbdaaSespiesub install($self, $state)
3868968f6aaSespie{
387ab64361eSespie	$self->SUPER::install($state);
388e451b54dSespie	my $auth = $self->name;
3897e83eca3Sespie	$state->say("adding #1 #2", $self->type, $auth) if $state->verbose >= 2;
3908968f6aaSespie	return if $state->{not};
3913511fba3Sespie	return if defined $self->{okay};
3928968f6aaSespie	my $l=[];
393e74b30b0Sespie	push(@$l, "-v") if $state->verbose >= 2;
3943511fba3Sespie	$self->build_args($l);
395dfdce8c8Sespie	$state->vsystem($self->command,, @$l, '--', $auth);
3963511fba3Sespie}
3973511fba3Sespie
3983511fba3Sespiepackage OpenBSD::PackingElement::NewUser;
3993511fba3Sespie
400039cbdaaSespiesub command($) { OpenBSD::Paths->useradd }
4013511fba3Sespie
402039cbdaaSespiesub build_args($self, $l)
4033511fba3Sespie{
4048968f6aaSespie	$self->add_entry($l,
4058968f6aaSespie	    '-u', $self->{uid},
4068968f6aaSespie	    '-g', $self->{group},
4078968f6aaSespie	    '-L', $self->{class},
4088968f6aaSespie	    '-c', $self->{comment},
4098968f6aaSespie	    '-d', $self->{home},
4108968f6aaSespie	    '-s', $self->{shell});
4118968f6aaSespie}
4128968f6aaSespie
4138968f6aaSespiepackage OpenBSD::PackingElement::NewGroup;
4148968f6aaSespie
415039cbdaaSespiesub command($) { OpenBSD::Paths->groupadd }
4163511fba3Sespie
417039cbdaaSespiesub build_args($self, $l)
4188968f6aaSespie{
4198968f6aaSespie	$self->add_entry($l, '-g', $self->{gid});
4208968f6aaSespie}
4218968f6aaSespie
4228968f6aaSespiepackage OpenBSD::PackingElement::FileBase;
4238968f6aaSespieuse OpenBSD::Error;
4248968f6aaSespieuse File::Basename;
4258968f6aaSespieuse File::Path;
4262eca3cddSespieuse OpenBSD::Temp;
4278968f6aaSespie
428039cbdaaSespiesub find_extractible($self, $state, $wanted, $tied)
429cdd4712cSespie{
43002254ccdSespie	if ($self->{tieto} || $self->{link} || $self->{symlink}) {
431cdd4712cSespie		$tied->{$self->name} = $self;
432cdd4712cSespie	} else {
433cdd4712cSespie		$wanted->{$self->name} = $self;
434cdd4712cSespie	}
435cdd4712cSespie}
436cdd4712cSespie
437039cbdaaSespiesub prepare_for_addition($self, $state, $pkgname, $)
4383511fba3Sespie{
43935135148Sespie	my $fname = $self->retrieve_fullname($state, $pkgname);
4403511fba3Sespie	# check for collisions with existing stuff
4414c82e4b5Sespie	if ($state->vstat->exists($fname)) {
44285a4d1f9Sespie		push(@{$state->{colliding}}, $self);
44345571e80Sespie		$self->{newly_found} = $pkgname;
44485a4d1f9Sespie		$state->{problems}++;
4453511fba3Sespie		return;
4463511fba3Sespie	}
447*d6425245Sespie	return if $state->{regression}{stub} && $pkgname !~ /^quirks\-/;
44835135148Sespie	my $s = $state->vstat->add($fname,
44935135148Sespie	    $self->{tieto} ? 0 : $self->retrieve_size, $pkgname);
4503511fba3Sespie	return unless defined $s;
451645ff4e3Sespie	if ($s->ro) {
452268e18bfSespie		$s->report_ro($state, $fname);
4533511fba3Sespie	}
454f04afe28Sespie	if ($s->avail < 0) {
455268e18bfSespie		$s->report_overflow($state, $fname);
4563511fba3Sespie	}
4573511fba3Sespie}
4583511fba3Sespie
459039cbdaaSespiesub prepare_to_extract($self, $state, $file)
46021b58429Sespie{
461f04afe28Sespie	my $fullname = $self->fullname;
46221b58429Sespie	my $destdir = $state->{destdir};
46321b58429Sespie
464f04afe28Sespie	$file->{cwd} = $self->cwd;
465033b1a08Sespie	if (!$file->validate_meta($self)) {
4667e83eca3Sespie		$state->fatal("can't continue");
4671ce85c68Sespie	}
4688968f6aaSespie
469e451b54dSespie	$file->set_name($fullname);
4708968f6aaSespie	$file->{destdir} = $destdir;
471cdd4712cSespie}
472cdd4712cSespie
473039cbdaaSespiesub find_safe_dir($self, $state)
474674fe0a0Sespie{
475674fe0a0Sespie	# figure out a safe directory where to put the temp file
476948d0681Sespie
477948d0681Sespie	my $fullname = $self->fullname;
478948d0681Sespie	my $filename = $state->{destdir}.$fullname;
479674fe0a0Sespie	my $d = dirname($filename);
480d66db768Sespie	my $orig = $d;
481674fe0a0Sespie
482674fe0a0Sespie	# we go back up until we find an existing directory.
483674fe0a0Sespie	# hopefully this will be on the same file system.
484674fe0a0Sespie	my @candidates = ();
485674fe0a0Sespie	while (!-d $d) {
486674fe0a0Sespie		push(@candidates, $d);
487674fe0a0Sespie		$d = dirname($d);
488674fe0a0Sespie	}
489674fe0a0Sespie	# and now we try to go back down, creating the best path we can
490674fe0a0Sespie	while (@candidates > 0) {
491674fe0a0Sespie		my $c = pop @candidates;
492674fe0a0Sespie		last if -e $c; # okay this exists, but is not a directory
493674fe0a0Sespie		$d = $c;
494674fe0a0Sespie	}
495948d0681Sespie	if (!-e _ && !$state->{not}) {
496948d0681Sespie		$state->make_path($d, $fullname);
497948d0681Sespie	}
498d66db768Sespie	if ($state->{current_set}{simple_update} &&
499d66db768Sespie	    $d eq $orig &&
500d66db768Sespie	    !-e $filename) {
501d66db768Sespie		$self->{avoid_temp} = $filename;
502d66db768Sespie	}
503d66db768Sespie
504674fe0a0Sespie	return $d;
505674fe0a0Sespie}
506674fe0a0Sespie
507039cbdaaSespiesub create_temp($self, $d, $state)
508023a8480Sespie{
509948d0681Sespie	my $fullname = $self->fullname;
510023a8480Sespie	my ($fh, $tempname) = OpenBSD::Temp::permanent_file($d, "pkg");
511023a8480Sespie	$self->{tempname} = $tempname;
512ddd2e461Sespie	if (!defined $tempname) {
513ddd2e461Sespie		if ($state->allow_nonroot($fullname)) {
514ddd2e461Sespie			$state->errsay("Can't create temp file outside localbase for #1", $fullname);
51580bdda31Sespie			$state->errsay(OpenBSD::Temp->last_error);
516ddd2e461Sespie			return undef;
517ddd2e461Sespie		}
51880bdda31Sespie		$state->fatal(OpenBSD::Temp->last_error);
519ddd2e461Sespie	}
520023a8480Sespie	return ($fh, $tempname);
521023a8480Sespie}
522023a8480Sespie
523039cbdaaSespiesub may_create_temp($self, $d, $state)
524d66db768Sespie{
525d66db768Sespie	if ($self->{avoid_temp}) {
526d66db768Sespie		if (open(my $fh, '>', $self->{avoid_temp})) {
527d66db768Sespie			return ($fh, $self->{avoid_temp});
528d66db768Sespie		}
529d66db768Sespie	}
530d66db768Sespie	delete $self->{avoid_temp};
531d66db768Sespie	return $self->create_temp($d, $state);
532d66db768Sespie}
533d66db768Sespie
534039cbdaaSespiesub tie($self, $state)
535cdd4712cSespie{
536cdd4712cSespie	if (defined $self->{link} || defined $self->{symlink}) {
537cdd4712cSespie		return;
538cdd4712cSespie	}
539cdd4712cSespie
540cdd4712cSespie	$self->SUPER::extract($state);
541cdd4712cSespie
542948d0681Sespie	my $d = $self->find_safe_dir($state);
543d66db768Sespie	my $src = $self->{tieto}->realname($state);
544d66db768Sespie	my $dest = $self->realname($state);
545d66db768Sespie	if ($state->{current_set}{simple_update} && $src eq $dest) {
546d66db768Sespie		$state->say("No name change on tied file #1", $src)
547d66db768Sespie		    if $state->verbose >= 3;
548d66db768Sespie		$state->{current_set}{dont_delete}{$dest} = 1;
549d66db768Sespie		$self->{avoid_temp} = 1;
550d66db768Sespie		return;
551d66db768Sespie	}
552cdd4712cSespie	if ($state->{not}) {
5535fe97cfdSespie		$state->say("link #1 -> #2",
5545fe97cfdSespie		    $self->name, $d) if $state->verbose >= 3;
555cdd4712cSespie	} else {
556d66db768Sespie		my ($fh, $tempname) = $self->may_create_temp($d, $state);
557cdd4712cSespie
558ddd2e461Sespie		return if !defined $tempname;
559cdd4712cSespie		unlink($tempname);
5605fe97cfdSespie		$state->say("link #1 -> #2", $src, $tempname)
561cdd4712cSespie		    if $state->verbose >= 3;
562cdd4712cSespie		link($src, $tempname) || $state->copy_file($src, $tempname);
563cdd4712cSespie	}
5648968f6aaSespie}
5658968f6aaSespie
566d66db768Sespie
567039cbdaaSespiesub extract($self, $state, $file)
5682eca3cddSespie{
5692eca3cddSespie	$self->SUPER::extract($state);
5702eca3cddSespie
571948d0681Sespie	my $d = $self->find_safe_dir($state);
5722eca3cddSespie	if ($state->{not}) {
5735fe97cfdSespie		$state->say("extract #1 -> #2",
5745fe97cfdSespie		    $self->name, $d) if $state->verbose >= 3;
5752eca3cddSespie		$state->{archive}->skip;
5762eca3cddSespie	} else {
577d66db768Sespie		my ($fh, $filename) = $self->may_create_temp($d, $state);
578d66db768Sespie		if (!defined $filename) {
579ddd2e461Sespie			$state->{archive}->skip;
580ddd2e461Sespie			return;
581ddd2e461Sespie		}
5822eca3cddSespie
583d66db768Sespie		if ($self->{avoid_temp}) {
584d66db768Sespie			$state->{current_set}{dont_delete}{$filename} = 1;
585d66db768Sespie		}
586d66db768Sespie		$state->say("extract #1 -> #2", $self->name, $filename)
5875fe97cfdSespie		    if $state->verbose >= 3;
5882eca3cddSespie
589cdd4712cSespie
5902eca3cddSespie		if (!$file->isFile) {
5912eca3cddSespie			$state->fatal("can't extract #1, it's not a file",
5922eca3cddSespie			    $self->stringize);
5932eca3cddSespie		}
594b8f783bdSespie		$file->extract_to_fh($fh);
595d66db768Sespie		$self->may_check_digest($filename, $state);
5962eca3cddSespie	}
5972eca3cddSespie}
5982eca3cddSespie
599039cbdaaSespiesub install($self, $state)
6002eca3cddSespie{
6012eca3cddSespie	$self->SUPER::install($state);
6022eca3cddSespie	my $fullname = $self->fullname;
6032eca3cddSespie	my $destdir = $state->{destdir};
6042eca3cddSespie	if ($state->{not}) {
6052eca3cddSespie		$state->say("moving tempfile -> #1",
6062eca3cddSespie		    $destdir.$fullname) if $state->verbose >= 5;
6072eca3cddSespie		return;
6082eca3cddSespie	}
609ddd2e461Sespie	$state->make_path(dirname($destdir.$fullname), $fullname);
6102eca3cddSespie	if (defined $self->{link}) {
6112eca3cddSespie		link($destdir.$self->{link}, $destdir.$fullname);
6125406d593Sespie		$state->say("link #1 -> #2", $destdir.$self->{link},
6135406d593Sespie		    $destdir.$fullname) if $state->verbose >= 5;
6142eca3cddSespie	} elsif (defined $self->{symlink}) {
6152eca3cddSespie		symlink($self->{symlink}, $destdir.$fullname);
6165406d593Sespie		$state->say("symlink #1 -> #2", $self->{symlink},
6175406d593Sespie		    $destdir.$fullname) if $state->verbose >= 5;
6182eca3cddSespie	} else {
619d66db768Sespie		if (defined $self->{avoid_temp}) {
620d66db768Sespie			delete $self->{avoid_temp};
621d66db768Sespie		} else {
622ddd2e461Sespie			if (!defined $self->{tempname}) {
623ddd2e461Sespie				return if $state->allow_nonroot($fullname);
624ddd2e461Sespie				$state->fatal("No tempname for #1", $fullname);
625ddd2e461Sespie			}
6262eca3cddSespie			rename($self->{tempname}, $destdir.$fullname) or
6272eca3cddSespie			    $state->fatal("can't move #1 to #2: #3",
6282eca3cddSespie				$self->{tempname}, $fullname, $!);
6292eca3cddSespie			$state->say("moving #1 -> #2",
6302eca3cddSespie			    $self->{tempname}, $destdir.$fullname)
6312eca3cddSespie				if $state->verbose >= 5;
63295a49439Sespie			delete $self->{tempname};
6332eca3cddSespie		}
634d66db768Sespie	}
6352eca3cddSespie	$self->set_modes($state, $destdir.$fullname);
6362eca3cddSespie}
6372eca3cddSespie
6382fcee420Sespiepackage OpenBSD::PackingElement::Extra;
639039cbdaaSespiesub find_extractible($self, $state, $wanted, $tied)
6402fcee420Sespie{
6412fcee420Sespie	$state->{current_set}{known_extra}{$self->fullname} = 1;
6422fcee420Sespie}
6432fcee420Sespie
644df8a9707Sespiepackage OpenBSD::PackingElement::RcScript;
645039cbdaaSespiesub install($self, $state)
646df8a9707Sespie{
647df8a9707Sespie	$state->{add_rcscripts}{$self->fullname} = 1;
648df8a9707Sespie	$self->SUPER::install($state);
649df8a9707Sespie}
650df8a9707Sespie
6518968f6aaSespiepackage OpenBSD::PackingElement::Sample;
6528968f6aaSespieuse OpenBSD::Error;
6538968f6aaSespieuse File::Copy;
6548968f6aaSespie
655039cbdaaSespiesub prepare_for_addition($self, $state, $pkgname, $)
6563511fba3Sespie{
6573511fba3Sespie	if (!defined $self->{copyfrom}) {
6587e83eca3Sespie		$state->errsay("\@sample element #1 does not reference a valid file",
6597e83eca3Sespie		    $self->fullname);
6607a5146e9Sespie		$state->{problems}++;
6613511fba3Sespie	}
66285a4d1f9Sespie	my $fname = $state->{destdir}.$self->fullname;
663a2706d2aSespie	# If file already exists, we won't change it
6644c82e4b5Sespie	if ($state->vstat->exists($fname)) {
665a2706d2aSespie		return;
666a2706d2aSespie	}
667a2706d2aSespie	my $size = $self->{copyfrom}->{size};
668b6ce8483Sespie	my $s = $state->vstat->add($fname, $size, $pkgname);
669a2706d2aSespie	return unless defined $s;
670645ff4e3Sespie	if ($s->ro) {
671268e18bfSespie		$s->report_ro($state, $fname);
672a2706d2aSespie	}
673f04afe28Sespie	if ($s->avail < 0) {
674268e18bfSespie		$s->report_overflow($state, $fname);
675a2706d2aSespie	}
6763511fba3Sespie}
6773511fba3Sespie
678039cbdaaSespiesub find_extractible($self, $state, $wanted, $tied)
679cdd4712cSespie{
6802fcee420Sespie	$state->{current_set}{known_sample}{$self->fullname} = 1;
681cdd4712cSespie}
682cdd4712cSespie
6837c22bbb7Sespie# $self->extract($state)
684039cbdaaSespiesub extract($, $)
6852eca3cddSespie{
6862eca3cddSespie}
6872eca3cddSespie
688039cbdaaSespiesub install($self, $state)
6898968f6aaSespie{
690ab64361eSespie	$self->SUPER::install($state);
6918968f6aaSespie	my $destdir = $state->{destdir};
692f04afe28Sespie	my $filename = $destdir.$self->fullname;
6938968f6aaSespie	my $orig = $self->{copyfrom};
694f04afe28Sespie	my $origname = $destdir.$orig->fullname;
6958968f6aaSespie	if (-e $filename) {
696e74b30b0Sespie		if ($state->verbose) {
6977e83eca3Sespie		    $state->say("The existing file #1 has NOT been changed",
6987e83eca3Sespie		    	$filename);
699700d29cbSespie		    if (defined $orig->{d}) {
7008968f6aaSespie
701c742d81aSespie			# XXX assume this would be the same type of file
702700d29cbSespie			my $d = $self->compute_digest($filename, $orig->{d});
703700d29cbSespie			if ($d->equals($orig->{d})) {
7047e83eca3Sespie			    $state->say("(but it seems to match the sample file #1)", $origname);
7058968f6aaSespie			} else {
7067e83eca3Sespie			    $state->say("It does NOT match the sample file #1",
7077e83eca3Sespie				$origname);
708662ab648Sespie			    $state->say("You may wish to update it manually");
7098968f6aaSespie			}
7108968f6aaSespie		    }
7118968f6aaSespie		}
7128968f6aaSespie	} else {
7138968f6aaSespie		if ($state->{not}) {
7147e83eca3Sespie			$state->say("The file #1 would be installed from #2",
7157e83eca3Sespie			    $filename, $origname) if $state->verbose >= 2;
7168968f6aaSespie		} else {
7178968f6aaSespie			if (!copy($origname, $filename)) {
7187e83eca3Sespie				$state->errsay("File #1 could not be installed:\n\t#2", $filename, $!);
7198968f6aaSespie			}
7205f05097cSespie			$self->set_modes($state, $filename);
721e74b30b0Sespie			if ($state->verbose >= 2) {
7227e83eca3Sespie			    $state->say("installed #1 from #2",
7237e83eca3Sespie				$filename, $origname);
7248968f6aaSespie			}
7258968f6aaSespie		}
7268968f6aaSespie	}
7278968f6aaSespie}
7288968f6aaSespie
7298968f6aaSespiepackage OpenBSD::PackingElement::Sampledir;
730039cbdaaSespiesub extract($, $)
7312eca3cddSespie{
7322eca3cddSespie}
7338968f6aaSespie
734039cbdaaSespiesub install	# forwarder
7358968f6aaSespie{
7368968f6aaSespie	&OpenBSD::PackingElement::Dir::install;
7378968f6aaSespie}
7388968f6aaSespie
7398968f6aaSespiepackage OpenBSD::PackingElement::Mandir;
7408968f6aaSespie
741039cbdaaSespiesub install($self, $state)
7428968f6aaSespie{
7438968f6aaSespie	$self->SUPER::install($state);
744d957d6edSespie	if (!$state->{current_set}{known_mandirs}{$self->fullname}) {
745c9c651ecSespie		$state->log("You may wish to add #1 to /etc/man.conf",
746c9c651ecSespie		    $self->fullname);
747c9c651ecSespie	}
7488968f6aaSespie}
7498968f6aaSespie
7508968f6aaSespiepackage OpenBSD::PackingElement::Manpage;
7518968f6aaSespie
752039cbdaaSespiesub install($self, $state)
7538968f6aaSespie{
7548968f6aaSespie	$self->SUPER::install($state);
755005e7d56Sespie	$self->register_manpage($state, 'addman');
7568968f6aaSespie}
7578968f6aaSespie
7588968f6aaSespiepackage OpenBSD::PackingElement::InfoFile;
7598968f6aaSespieuse File::Basename;
7608968f6aaSespieuse OpenBSD::Error;
7618968f6aaSespie
762039cbdaaSespiesub install($self, $state)
7638968f6aaSespie{
7648968f6aaSespie	$self->SUPER::install($state);
7658968f6aaSespie	return if $state->{not};
766f04afe28Sespie	my $fullname = $state->{destdir}.$self->fullname;
767b5458979Sespie	$state->vsystem(OpenBSD::Paths->install_info,
768dfdce8c8Sespie	    "--info-dir=".dirname($fullname), '--', $fullname);
7698968f6aaSespie}
7708968f6aaSespie
7718968f6aaSespiepackage OpenBSD::PackingElement::Shell;
772039cbdaaSespiesub install($self, $state)
7738968f6aaSespie{
7748968f6aaSespie	$self->SUPER::install($state);
7758968f6aaSespie	return if $state->{not};
776f04afe28Sespie	my $fullname = $self->fullname;
7778968f6aaSespie	my $destdir = $state->{destdir};
7788968f6aaSespie	# go append to /etc/shells if needed
77973c16894Sespie	open(my $shells, '<', $destdir.OpenBSD::Paths->shells) or return;
7808968f6aaSespie	while(<$shells>) {
781cc24e6f2Sespie		s/^\#.*//o;
782b62674ebSespie		return if m/^\Q$fullname\E\s*$/;
7838968f6aaSespie	}
7848968f6aaSespie	close($shells);
78573c16894Sespie	open(my $shells2, '>>', $destdir.OpenBSD::Paths->shells) or return;
7868968f6aaSespie	print $shells2 $fullname, "\n";
7878968f6aaSespie	close $shells2;
7887e83eca3Sespie	$state->say("Shell #1 appended to #2", $fullname,
7897e83eca3Sespie	    $destdir.OpenBSD::Paths->shells) if $state->verbose;
7908968f6aaSespie}
7918968f6aaSespie
7928968f6aaSespiepackage OpenBSD::PackingElement::Dir;
793039cbdaaSespiesub extract($self, $state)
7942eca3cddSespie{
7952eca3cddSespie	my $fullname = $self->fullname;
7962eca3cddSespie	my $destdir = $state->{destdir};
7972eca3cddSespie
7982eca3cddSespie	return if -e $destdir.$fullname;
7992eca3cddSespie	$self->SUPER::extract($state);
8002eca3cddSespie	$state->say("new directory #1", $destdir.$fullname)
8012eca3cddSespie	    if $state->verbose >= 3;
8022eca3cddSespie	return if $state->{not};
803ddd2e461Sespie	$state->make_path($destdir.$fullname, $fullname);
8042eca3cddSespie}
8052eca3cddSespie
806039cbdaaSespiesub install($self, $state)
8078968f6aaSespie{
808ab64361eSespie	$self->SUPER::install($state);
809f04afe28Sespie	my $fullname = $self->fullname;
8108968f6aaSespie	my $destdir = $state->{destdir};
8118968f6aaSespie
8122eca3cddSespie	$state->say("new directory #1", $destdir.$fullname)
8132eca3cddSespie	    if $state->verbose >= 5;
8148968f6aaSespie	return if $state->{not};
815ddd2e461Sespie	$state->make_path($destdir.$fullname, $fullname);
8165f05097cSespie	$self->set_modes($state, $destdir.$fullname);
8178968f6aaSespie}
8188968f6aaSespie
8198968f6aaSespiepackage OpenBSD::PackingElement::Exec;
8208968f6aaSespieuse OpenBSD::Error;
8218968f6aaSespie
822039cbdaaSespiesub install($self, $state)
8238968f6aaSespie{
824ab64361eSespie	$self->SUPER::install($state);
8257947b3f2Sespie	if ($self->should_run($state)) {
8268968f6aaSespie		$self->run($state);
8278968f6aaSespie	}
8287947b3f2Sespie}
8297947b3f2Sespie
830039cbdaaSespiesub should_run($, $) { 1 }
8317947b3f2Sespie
8327947b3f2Sespiepackage OpenBSD::PackingElement::ExecAdd;
833039cbdaaSespiesub should_run($self, $state)
8347947b3f2Sespie{
83564061489Sespie	return !$state->replacing;
8367947b3f2Sespie}
8377947b3f2Sespie
8387947b3f2Sespiepackage OpenBSD::PackingElement::ExecUpdate;
839039cbdaaSespiesub should_run($self, $state)
8407947b3f2Sespie{
84164061489Sespie	return $state->replacing;
8427947b3f2Sespie}
8438968f6aaSespie
844eb082087Sespiepackage OpenBSD::PackingElement::Tag;
845eb082087Sespie
846039cbdaaSespiesub install($self, $state)
847eb082087Sespie{
848eb082087Sespie	for my $d (@{$self->{definition_list}}) {
849eb082087Sespie		$d->add_tag($self, "install", $state);
850eb082087Sespie	}
851eb082087Sespie}
852eb082087Sespie
8538968f6aaSespiepackage OpenBSD::PackingElement::Lib;
8548968f6aaSespie
855039cbdaaSespiesub install($self, $state)
8568968f6aaSespie{
8578968f6aaSespie	$self->SUPER::install($state);
858a6526ca6Sespie	$self->mark_ldconfig_directory($state);
8598968f6aaSespie}
8608968f6aaSespie
8613511fba3Sespiepackage OpenBSD::PackingElement::SpecialFile;
8623511fba3Sespieuse OpenBSD::PackageInfo;
8633511fba3Sespieuse OpenBSD::Error;
8643511fba3Sespie
865039cbdaaSespiesub copy_info($self, $dest, $state)
866c2569c13Sespie{
867c2569c13Sespie	require File::Copy;
868c2569c13Sespie
8698e2dcc0dSespie	File::Copy::move($self->fullname, $dest) or
8708d5333baSespie	    $state->errsay("Problem while moving #1 into #2: #3",
8718d5333baSespie		$self->fullname, $dest, $!);
872c2569c13Sespie}
873c2569c13Sespie
874039cbdaaSespiesub extract($self, $state)
8754119eea1Sespie{
8764119eea1Sespie	$self->may_verify_digest($state);
8774119eea1Sespie}
8784119eea1Sespie
879039cbdaaSespiesub find_extractible($self, $state, $, $)
8800c7be895Sespie{
8810c7be895Sespie	$self->may_verify_digest($state);
8820c7be895Sespie}
8830c7be895Sespie
884c2569c13Sespiepackage OpenBSD::PackingElement::FCONTENTS;
885039cbdaaSespiesub copy_info($, $, $)
886c2569c13Sespie{
887c2569c13Sespie}
888c2569c13Sespie
889062c25acSespiepackage OpenBSD::PackingElement::AskUpdate;
890039cbdaaSespiesub prepare_for_addition($self, $state, $pkgname, $set)
891062c25acSespie{
892062c25acSespie	my @old = $set->older_names;
893062c25acSespie	if ($self->spec->match_ref(\@old) > 0) {
894062c25acSespie		my $key = "update_".OpenBSD::PackageName::splitstem($pkgname);
895a409537dSespie		return if $state->defines($key);
896e8cdaf99Sespie		if ($state->is_interactive) {
897fb75580aSespie			if ($state->confirm_defaults_to_no(
898fb75580aSespie			    "#1: #2.\nDo you want to update now",
899fb75580aSespie			    $pkgname, $self->{message})) {
900062c25acSespie			    	return;
901062c25acSespie			}
902062c25acSespie		} else {
9037e83eca3Sespie			$state->errsay("Can't update #1 now: #2",
9047e83eca3Sespie			    $pkgname, $self->{message});
905062c25acSespie		}
906062c25acSespie		$state->{problems}++;
907062c25acSespie	}
908062c25acSespie}
909062c25acSespie
910c9c651ecSespiepackage OpenBSD::PackingElement::FDISPLAY;
911039cbdaaSespiesub install($self, $state)
912c9c651ecSespie{
913c9c651ecSespie	my $d = $self->{d};
914d957d6edSespie	if (!$state->{current_set}{known_displays}{$self->{d}->key}) {
915c9c651ecSespie		$self->prepare($state);
916c9c651ecSespie	}
917c9c651ecSespie	$self->SUPER::install($state);
918c9c651ecSespie}
919c9c651ecSespie
920c9c651ecSespiepackage OpenBSD::PackingElement::FUNDISPLAY;
921039cbdaaSespiesub find_extractible($self, $state, $wanted, $tied)
922c9c651ecSespie{
923d957d6edSespie	$state->{current_set}{known_displays}{$self->{d}->key} = 1;
924c9c651ecSespie	$self->SUPER::find_extractible($state, $wanted, $tied);
925c9c651ecSespie}
926c9c651ecSespie
9278968f6aaSespie1;
928