1cb05967dSespie# ex:ts=8 sw=4:
2*039cbdaaSespie# $OpenBSD: UpdateSet.pm,v 1.89 2023/06/13 09:07:17 espie Exp $
3cb05967dSespie#
463e11790Sespie# Copyright (c) 2007-2010 Marc Espie <espie@openbsd.org>
5cb05967dSespie#
6cb05967dSespie# Permission to use, copy, modify, and distribute this software for any
7cb05967dSespie# purpose with or without fee is hereby granted, provided that the above
8cb05967dSespie# copyright notice and this permission notice appear in all copies.
9cb05967dSespie#
10cb05967dSespie# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11cb05967dSespie# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12cb05967dSespie# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13cb05967dSespie# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14cb05967dSespie# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15cb05967dSespie# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16cb05967dSespie# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17cb05967dSespie
1859730165Sespie
1959730165Sespie# an UpdateSet is a list of packages to remove/install.
202172b28bSespie# it contains several things:
2159730165Sespie# -> a list of older packages to remove (installed locations)
2259730165Sespie# -> a list of newer packages to add (might be very simple locations)
2359730165Sespie# -> a list of "hints", as package names to install
242172b28bSespie# -> a list of packages that are kept throughout an update
2559730165Sespie# every add/remove operations manipulate UpdateSet.
2659730165Sespie#
2759730165Sespie# Since older packages are always installed, they're organized as a hash.
2859730165Sespie#
2959730165Sespie# XXX: an UpdateSet succeeds or fails "together".
3059730165Sespie# if several packages should be removed/added, then not being able
3159730165Sespie# to do stuff on ONE of them is enough to invalidate the whole set.
3259730165Sespie#
3359730165Sespie# Normal UpdateSets contain one newer package at most.
3459730165Sespie# Bigger UpdateSets can be created through the merge operation, which
3559730165Sespie# will be used only when necessary.
362172b28bSespie#
372172b28bSespie# kept packages are needed after merges, where some dependencies may
382172b28bSespie# not need updating, and to distinguish from old packages that will be
392172b28bSespie# removed.
402172b28bSespie#
412172b28bSespie# for instance, package installation will check UpdateSets for internal
422172b28bSespie# dependencies and for conflicts. For that to work, we need kept stuff
432172b28bSespie#
44*039cbdaaSespieuse v5.36;
45f76cebffSespie
464c82e4b5Sespie# hints should behave like locations
474c82e4b5Sespiepackage OpenBSD::hint;
48*039cbdaaSespiesub new($class, $name)
494c82e4b5Sespie{
504c82e4b5Sespie	bless {name => $name}, $class;
514c82e4b5Sespie}
524c82e4b5Sespie
53*039cbdaaSespiesub pkgname($self)
544c82e4b5Sespie{
55*039cbdaaSespie	return $self->{name};
564c82e4b5Sespie}
574c82e4b5Sespie
58e05ce761Sespiepackage OpenBSD::hint2;
59e05ce761Sespieour @ISA = qw(OpenBSD::hint);
60e05ce761Sespie
61a0ae1cf1Sespie# Code organisation: this is the stuff that's common for actual UpdateSets
62a0ae1cf1Sespie# (used by pkg_add) and DeleteSets, a simpler version used by pkg_delete.
63a0ae1cf1Sespie# Turns out some of that stuff is identical.
64a0ae1cf1Sespie# The really juicy stuff resides in pkg_add/pkg_delete proper.
65d72bb06fSespiepackage OpenBSD::DeleteSet;
661344a963Sespieuse OpenBSD::Error;
671344a963Sespie
68*039cbdaaSespiesub new($class, $state)
69d72bb06fSespie{
70d72bb06fSespie	return bless {older => {}}, $class;
71d72bb06fSespie}
72d72bb06fSespie
73*039cbdaaSespiesub add_older($self, @p)
74d72bb06fSespie{
75*039cbdaaSespie	for my $h (@p) {
769a9cac3bSespie		$self->{older}{$h->pkgname} = $h;
779a9cac3bSespie		$h->{is_old} = 1;
78d72bb06fSespie	}
79d72bb06fSespie	return $self;
80d72bb06fSespie}
81d72bb06fSespie
82*039cbdaaSespiesub older($self)
83d72bb06fSespie{
84d72bb06fSespie	return values %{$self->{older}};
85d72bb06fSespie}
86d72bb06fSespie
87*039cbdaaSespiesub older_names($self)
88d72bb06fSespie{
89d72bb06fSespie	return keys %{$self->{older}};
90d72bb06fSespie}
91d72bb06fSespie
92*039cbdaaSespiesub all_handles	# forwarder
93d72bb06fSespie{
94d72bb06fSespie	&older;
95d72bb06fSespie}
96d72bb06fSespie
97*039cbdaaSespiesub changed_handles	# forwarder
98f3ff8f9eSespie{
99f3ff8f9eSespie	&older;
100f3ff8f9eSespie}
101f3ff8f9eSespie
102*039cbdaaSespiesub mark_as_finished($self)
103d72bb06fSespie{
104d72bb06fSespie	$self->{finished} = 1;
105d72bb06fSespie}
106d72bb06fSespie
107*039cbdaaSespiesub cleanup($self, $error = undef, $errorinfo = undef)
108d72bb06fSespie{
109d72bb06fSespie	for my $h ($self->all_handles) {
110d72bb06fSespie		$h->cleanup($error, $errorinfo);
111d72bb06fSespie	}
112a92fd548Sespie	if (defined $error) {
113d72bb06fSespie		$self->{error} //= $error;
114d72bb06fSespie		$self->{errorinfo} //= $errorinfo;
115a92fd548Sespie	}
1163dafbabdSespie	delete $self->{solver};
117d957d6edSespie	delete $self->{known_mandirs};
118d957d6edSespie	delete $self->{known_displays};
119d66db768Sespie	delete $self->{dont_delete};
1202fcee420Sespie	delete $self->{known_extra};
1212fcee420Sespie	delete $self->{known_sample};
122d72bb06fSespie	$self->mark_as_finished;
123d72bb06fSespie}
124d72bb06fSespie
125*039cbdaaSespiesub has_error	# forwarder
126d72bb06fSespie{
127d72bb06fSespie	&OpenBSD::Handle::has_error;
128d72bb06fSespie}
129d72bb06fSespie
130a0ae1cf1Sespie# display code that will put together packages with the same version
131*039cbdaaSespiesub smart_join($self, @p)
13275ad74c9Sespie{
133*039cbdaaSespie	if (@p <= 1) {
134*039cbdaaSespie		return join('+', @p);
13575ad74c9Sespie	}
13675ad74c9Sespie	my ($k, @stems);
137*039cbdaaSespie	for my $l (@p) {
13875ad74c9Sespie		my ($stem, @rest) = OpenBSD::PackageName::splitname($l);
13975ad74c9Sespie		my $k2 = join('-', @rest);
14075ad74c9Sespie		$k //= $k2;
14175ad74c9Sespie		if ($k2 ne $k) {
142*039cbdaaSespie			return join('+', sort @p);
14375ad74c9Sespie		}
14475ad74c9Sespie		push(@stems, $stem);
14575ad74c9Sespie	}
14675ad74c9Sespie	return join('+', sort @stems).'-'.$k;
14775ad74c9Sespie}
14875ad74c9Sespie
149*039cbdaaSespiesub print($self)
150d72bb06fSespie{
15175ad74c9Sespie	return $self->smart_join($self->older_names);
152d72bb06fSespie}
153d72bb06fSespie
154*039cbdaaSespiesub todo_names	# forwarder
155d72bb06fSespie{
156d72bb06fSespie	&older_names;
157d72bb06fSespie}
158d72bb06fSespie
159*039cbdaaSespiesub short_print($self)
160d72bb06fSespie{
16175ad74c9Sespie	my $result = $self->smart_join($self->todo_names);
162d72bb06fSespie	if (length $result > 30) {
163d72bb06fSespie		return substr($result, 0, 27)."...";
164d72bb06fSespie	} else {
165d72bb06fSespie		return $result;
166d72bb06fSespie	}
167d72bb06fSespie}
168d72bb06fSespie
169*039cbdaaSespiesub real_set($set)
170d72bb06fSespie{
171d72bb06fSespie	while (defined $set->{merged}) {
172d72bb06fSespie		$set = $set->{merged};
173d72bb06fSespie	}
174d72bb06fSespie	return $set;
175d72bb06fSespie}
176d72bb06fSespie
177*039cbdaaSespiesub merge_set($self, $set)
178d72bb06fSespie{
179d72bb06fSespie	$self->add_older($set->older);
180d72bb06fSespie	$set->mark_as_finished;
181d72bb06fSespie	# XXX and mark it as merged, for eventual updates
182d72bb06fSespie	$set->{merged} = $self;
183d72bb06fSespie}
184d72bb06fSespie
185d72bb06fSespie# Merge several deletesets together
186*039cbdaaSespiesub merge($self, $tracker, @sets)
187d72bb06fSespie{
188d72bb06fSespie	# Apparently simple, just add the missing parts
189d72bb06fSespie	for my $set (@sets) {
190d72bb06fSespie		next if $set eq $self;
191d72bb06fSespie		$self->merge_set($set);
192d72bb06fSespie		$tracker->handle_set($set);
193d72bb06fSespie	}
194d72bb06fSespie	# then regen tracker info for $self
195d72bb06fSespie	$tracker->todo($self);
196d72bb06fSespie	return $self;
197d72bb06fSespie}
198d72bb06fSespie
199*039cbdaaSespiesub match_locations($, @)
200b8b880d2Sespie{
201b8b880d2Sespie	return [];
202b8b880d2Sespie}
203b8b880d2Sespie
2041344a963SespieOpenBSD::Auto::cache(solver,
205*039cbdaaSespie    sub($self) {
2061344a963Sespie    	require OpenBSD::Dependencies;
207*039cbdaaSespie	return OpenBSD::Dependencies::Solver->new($self);
2081344a963Sespie    });
2091344a963Sespie
2101344a963SespieOpenBSD::Auto::cache(conflict_cache,
211*039cbdaaSespie    sub($) {
2121344a963Sespie    	require OpenBSD::Dependencies;
2131344a963Sespie	return OpenBSD::ConflictCache->new;
2141344a963Sespie    });
2151344a963Sespie
21616008a12Sespiepackage OpenBSD::UpdateSet;
217d72bb06fSespieour @ISA = qw(OpenBSD::DeleteSet);
21898cf79f0Sespie
219*039cbdaaSespiesub new($class, $state)
22016008a12Sespie{
221d72bb06fSespie	my $o = $class->SUPER::new($state);
222d72bb06fSespie	$o->{newer} = {};
223d72bb06fSespie	$o->{kept} = {};
224d72bb06fSespie	$o->{repo} = $state->repo;
225d72bb06fSespie	$o->{hints} = [];
226d72bb06fSespie	$o->{updates} = 0;
227d72bb06fSespie	return $o;
22816008a12Sespie}
22916008a12Sespie
230*039cbdaaSespie# TODO this stuff is mostly unused right now (or buggy)
231*039cbdaaSespiesub path($set)
2327087d2adSespie{
2337087d2adSespie	return $set->{path};
2347087d2adSespie}
2357087d2adSespie
236*039cbdaaSespiesub add_repositories($set, @repos)
2377087d2adSespie{
2387087d2adSespie	if (!defined $set->{path}) {
23968a8a69cSespie		$set->{path} = $set->{repo}->path;
2407087d2adSespie	}
2417087d2adSespie	$set->{path}->add(@repos);
2427087d2adSespie}
2437087d2adSespie
244*039cbdaaSespiesub merge_paths($set, $other)
24546077820Sespie{
24646077820Sespie	if (defined $other->path) {
24746077820Sespie		if (!defined $set->path) {
24846077820Sespie			$set->{path} = $other->path;
24946077820Sespie		} elsif ($set->{path} ne $other->path) {
25046077820Sespie			$set->add_path(@{$other->{path}});
25146077820Sespie		}
25246077820Sespie	}
25346077820Sespie}
25446077820Sespie
255*039cbdaaSespiesub match_locations($set, @spec)
2567087d2adSespie{
2577087d2adSespie	my $r = [];
2587087d2adSespie	if (defined $set->{path}) {
2597087d2adSespie		$r = $set->{path}->match_locations(@spec);
2607087d2adSespie	}
2617087d2adSespie	if (@$r == 0) {
26268a8a69cSespie		$r = $set->{repo}->match_locations(@spec);
2637087d2adSespie	}
2647087d2adSespie	return $r;
2657087d2adSespie}
2667087d2adSespie
267*039cbdaaSespiesub add_newer($self, @p)
26816008a12Sespie{
269*039cbdaaSespie	for my $h (@p) {
2709a9cac3bSespie		$self->{newer}{$h->pkgname} = $h;
2710849d6feSespie		$self->{updates}++;
272e05ce761Sespie	}
273e05ce761Sespie	return $self;
274e05ce761Sespie}
275e05ce761Sespie
276*039cbdaaSespiesub add_kept($self, @p)
2771ac97267Sespie{
278*039cbdaaSespie	for my $h (@p) {
2791ac97267Sespie		$self->{kept}->{$h->pkgname} = $h;
2801ac97267Sespie	}
2811ac97267Sespie	return $self;
2821ac97267Sespie}
2831ac97267Sespie
284*039cbdaaSespiesub move_kept($self, @p)
2852172b28bSespie{
286*039cbdaaSespie	for my $h (@p) {
2872c3f8ba0Sespie		delete $self->{older}{$h->pkgname};
2882c3f8ba0Sespie		delete $self->{newer}{$h->pkgname};
2892c3f8ba0Sespie		$self->{kept}{$h->pkgname} = $h;
2907571f4ebSespie		if (!defined $h->{location}) {
2917571f4ebSespie			$h->{location} =
2927571f4ebSespie			    $self->{repo}->installed->find($h->pkgname);
2937571f4ebSespie		}
294bbe3831fSespie		$h->complete_dependency_info;
295bbe3831fSespie		$h->{update_found} = $h;
2962172b28bSespie	}
2972172b28bSespie	return $self;
2982172b28bSespie}
2992172b28bSespie
300*039cbdaaSespiesub add_hints($self, @p)
301298306aeSespie{
302*039cbdaaSespie	for my $h (@p) {
303298306aeSespie		push(@{$self->{hints}}, OpenBSD::hint->new($h));
304298306aeSespie	}
305298306aeSespie	return $self;
306298306aeSespie}
307298306aeSespie
308*039cbdaaSespiesub add_hints2($self, @p)
309298306aeSespie{
310*039cbdaaSespie	for my $h (@p) {
311298306aeSespie		push(@{$self->{hints}}, OpenBSD::hint2->new($h));
312298306aeSespie	}
313298306aeSespie	return $self;
314298306aeSespie}
315298306aeSespie
316*039cbdaaSespiesub newer($self)
31716008a12Sespie{
318298306aeSespie	return values %{$self->{newer}};
31916008a12Sespie}
32016008a12Sespie
321*039cbdaaSespiesub kept($self)
3222172b28bSespie{
3232172b28bSespie	return values %{$self->{kept}};
3242172b28bSespie}
3252172b28bSespie
326*039cbdaaSespiesub hints($self)
3277d63c3f8Sespie{
3287d63c3f8Sespie	return @{$self->{hints}};
3297d63c3f8Sespie}
3307d63c3f8Sespie
331*039cbdaaSespiesub newer_names($self)
332dbfb28daSespie{
333298306aeSespie	return keys %{$self->{newer}};
334298306aeSespie}
335298306aeSespie
336*039cbdaaSespiesub kept_names($self)
3372172b28bSespie{
3382172b28bSespie	return keys %{$self->{kept}};
3392172b28bSespie}
3402172b28bSespie
341*039cbdaaSespiesub all_handles($self)
342d72bb06fSespie{
343d72bb06fSespie	return ($self->older, $self->newer, $self->kept);
344d72bb06fSespie}
345d72bb06fSespie
346*039cbdaaSespiesub changed_handles($self)
347f3ff8f9eSespie{
348f3ff8f9eSespie	return ($self->older, $self->newer);
349f3ff8f9eSespie}
350f3ff8f9eSespie
351*039cbdaaSespiesub hint_names($self)
352298306aeSespie{
353298306aeSespie	return map {$_->pkgname} $self->hints;
35416008a12Sespie}
35516008a12Sespie
356*039cbdaaSespiesub older_to_do($self)
35716008a12Sespie{
35816008a12Sespie	# XXX in `combined' updates, some dependencies may remove extra
35916008a12Sespie	# packages, so we do a double-take on the list of packages we
36016008a12Sespie	# are actually replacing... for now, until we merge update sets.
36116008a12Sespie	require OpenBSD::PackageInfo;
36216008a12Sespie	my @l = ();
36316008a12Sespie	for my $h ($self->older) {
3642172b28bSespie		if (OpenBSD::PackageInfo::is_installed($h->pkgname)) {
36516008a12Sespie			push(@l, $h);
36616008a12Sespie		}
36716008a12Sespie	}
36816008a12Sespie	return @l;
36916008a12Sespie}
37016008a12Sespie
371*039cbdaaSespiesub print($self)
37216008a12Sespie{
373475c1fd5Sespie	my $result = "";
3742172b28bSespie	if ($self->kept > 0) {
37575ad74c9Sespie		$result = "[".$self->smart_join($self->kept_names)."]";
37675ad74c9Sespie	}
37775ad74c9Sespie	my ($old, $new);
37875ad74c9Sespie	if ($self->older > 0) {
37975ad74c9Sespie		$old = $self->SUPER::print;
38075ad74c9Sespie	}
38175ad74c9Sespie	if ($self->newer > 0) {
38275ad74c9Sespie		$new = $self->smart_join($self->newer_names);
3832172b28bSespie	}
3840cc88a49Sespie	# XXX common case
38575ad74c9Sespie	if (defined $old && defined $new) {
3860fc00915Sespie		my ($stema, @resta) = OpenBSD::PackageName::splitname($old);
3870fc00915Sespie		my $resta = join('-', @resta);
3880fc00915Sespie		my ($stemb, @restb) = OpenBSD::PackageName::splitname($new);
3890fc00915Sespie		my $restb = join('-', @restb);
3900fc00915Sespie		if ($stema eq $stemb && $resta !~ /\+/ && $restb !~ /\+/) {
3910fc00915Sespie			return $result .$old."->".$restb;
3920cc88a49Sespie		}
3930cc88a49Sespie	}
3940cc88a49Sespie
39575ad74c9Sespie	if (defined $old) {
39675ad74c9Sespie		$result .= $old."->";
39716008a12Sespie	}
39875ad74c9Sespie	if (defined $new) {
39975ad74c9Sespie		$result .= $new;
400475c1fd5Sespie	} elsif ($self->hints > 0) {
40175ad74c9Sespie		$result .= $self->smart_join($self->hint_names);
402475c1fd5Sespie	}
403475c1fd5Sespie	return $result;
40416008a12Sespie}
40516008a12Sespie
406*039cbdaaSespiesub todo_names($self)
407b07acb4aSespie{
4082a5be836Sespie	if ($self->newer > 0) {
4092a5be836Sespie		return $self->newer_names;
4102a5be836Sespie	} else {
4112a5be836Sespie		return $self->kept_names;
4122a5be836Sespie	}
413b1238d28Sespie}
414b1238d28Sespie
415*039cbdaaSespiesub validate_plists($self, $state)
41616008a12Sespie{
41716008a12Sespie	$state->{problems} = 0;
4181a1aaf83Sespie	delete $state->{overflow};
41916008a12Sespie
4208221480eSespie	$state->{current_set} = $self;
4218221480eSespie
42216008a12Sespie	for my $o ($self->older_to_do) {
42316008a12Sespie		require OpenBSD::Delete;
42416008a12Sespie		OpenBSD::Delete::validate_plist($o->{plist}, $state);
42516008a12Sespie	}
42616008a12Sespie	$state->{colliding} = [];
42716008a12Sespie	for my $n ($self->newer) {
42816008a12Sespie		require OpenBSD::Add;
429062c25acSespie		OpenBSD::Add::validate_plist($n->{plist}, $state, $self);
43016008a12Sespie	}
43116008a12Sespie	if (@{$state->{colliding}} > 0) {
43216008a12Sespie		require OpenBSD::CollisionReport;
43316008a12Sespie
434b5c9b1b8Sespie		OpenBSD::CollisionReport::collision_report($state->{colliding}, $state, $self);
43516008a12Sespie	}
43616008a12Sespie	if (defined $state->{overflow}) {
4374c82e4b5Sespie		$state->vstat->tally;
4383c675cdaSespie		$state->vstat->drop_changes;
4393c675cdaSespie		# nothing to try if we don't have existing stuff to remove
4403c675cdaSespie		return 0 if $self->older == 0;
4413c675cdaSespie		# we already tried the other way around...
4423c675cdaSespie		return 0 if $state->{delete_first};
443a409537dSespie		if ($state->defines('deletefirst') ||
444fb75580aSespie		    $state->confirm_defaults_to_no(
445fb75580aSespie			"Delete older packages first")) {
4463c675cdaSespie			# okay we recurse doing things the other way around
4471a1aaf83Sespie			$state->{delete_first} = 1;
4481a1aaf83Sespie			return $self->validate_plists($state);
4491a1aaf83Sespie		}
4501a1aaf83Sespie	}
45116008a12Sespie	if ($state->{problems}) {
452671c958dSespie		$state->vstat->drop_changes;
453671c958dSespie		return 0;
454671c958dSespie	} else {
4554c82e4b5Sespie		$state->vstat->synchronize;
456671c958dSespie		return 1;
457671c958dSespie	}
45816008a12Sespie}
45916008a12Sespie
460*039cbdaaSespiesub cleanup_old_shared($set, $state)
4618221480eSespie{
4628221480eSespie	my $h = $set->{old_shared};
4638221480eSespie
4648221480eSespie	for my $d (sort {$b cmp $a} keys %$h) {
4658221480eSespie		OpenBSD::SharedItems::wipe_directory($state, $h, $d) ||
4668221480eSespie		    $state->fatal("Can't continue");
4678221480eSespie		delete $state->{recorder}{dirs}{$d};
4688221480eSespie	}
4698221480eSespie}
4708221480eSespie
471d72bb06fSespiemy @extra = qw(solver conflict_cache);
472*039cbdaaSespiesub mark_as_finished($self)
473d72bb06fSespie{
474d72bb06fSespie	for my $i (@extra, 'sha') {
475d72bb06fSespie		delete $self->{$i};
476d72bb06fSespie	}
477d72bb06fSespie	$self->SUPER::mark_as_finished;
478d72bb06fSespie}
479d72bb06fSespie
480*039cbdaaSespiesub merge_if_exists($self, $k, @extra)
4812bf3c32bSespie{
4821ac97267Sespie	my @list = ();
4831ac97267Sespie	for my $s (@extra) {
4841ac97267Sespie		if ($s ne $self && defined $s->{$k}) {
4851ac97267Sespie			push(@list, $s->{$k});
4862bf3c32bSespie		}
4872bf3c32bSespie	}
4881ac97267Sespie	$self->$k->merge(@list);
4891ac97267Sespie}
4902bf3c32bSespie
491*039cbdaaSespiesub merge_set($self, $set)
492d72bb06fSespie{
493d72bb06fSespie	$self->SUPER::merge_set($set);
494d72bb06fSespie	$self->add_newer($set->newer);
495d72bb06fSespie	$self->add_kept($set->kept);
496d72bb06fSespie	$self->merge_paths($set);
497d72bb06fSespie	$self->{updates} += $set->{updates};
498d72bb06fSespie	$set->{updates} = 0;
499d72bb06fSespie}
500d72bb06fSespie
5016af57ca2Sespie# Merge several updatesets together
502*039cbdaaSespiesub merge($self, $tracker, @sets)
5036af57ca2Sespie{
504d72bb06fSespie	for my $i (@extra) {
505d72bb06fSespie		$self->merge_if_exists($i, @sets);
5066af57ca2Sespie	}
507d72bb06fSespie	return $self->SUPER::merge($tracker, @sets);
508b07acb4aSespie}
509b07acb4aSespie
510cb05967dSespie1;
511