Tweaked version. The previous version was working just fine, but this one also marks hardlinks and symlinks as skippable, as should be. It also adjusts the pace of progress slightly (making the meter jump AFTER skipping the entry), and also it does prepare_to_extract systematically (which will recheck links vs their packing-list entry for consistency)
I haven't seen any negative report from the previous version. If this one does work too, I intend to commit it sometimes during the week. Index: OpenBSD/Add.pm =================================================================== RCS file: /build/data/openbsd/cvs/src/usr.sbin/pkg_add/OpenBSD/Add.pm,v retrieving revision 1.153 diff -u -p -r1.153 Add.pm --- OpenBSD/Add.pm 20 May 2014 05:55:43 -0000 1.153 +++ OpenBSD/Add.pm 13 Jun 2014 17:14:05 -0000 @@ -113,7 +113,34 @@ sub perform_extraction $state->{partial} = $handle->{partial}; $state->{archive} = $handle->{location}; $state->{check_digest} = $handle->{plist}{check_digest}; - $state->progress->visit_with_size($handle->{plist}, 'extract', $state); + my ($wanted, $tied) = {}; + $handle->{plist}->find_extractible($state, $wanted, $tied); + my $p = $state->progress->new_sizer($handle->{plist}, $state); + while (my $file = $state->{archive}->next) { + my $e = $tied->{$file->name}; + if (defined $e) { + delete $tied->{$file->name}; + $e->prepare_to_extract($state, $file); + $state->{archive}->skip; + $p->advance($e); + # skip to next; + } + $e = $wanted->{$file->name}; + if (!defined $e) { + $state->Fatal("archive member not found #1", + $file->name); + } + delete $wanted->{$file->name}; + $e->prepare_to_extract($state, $file); + $e->extract($state, $file); + $p->advance($e); + if (keys %$wanted == 0) { + return; + } + } + if (keys %$wanted > 0) { + $state->fatal("Truncated archive"); + } } my $user_tagged = {}; @@ -181,6 +208,10 @@ sub prepare_for_addition { } +sub find_extractible +{ +} + sub extract { my ($self, $state) = @_; @@ -236,6 +267,16 @@ sub set_modes } } +package OpenBSD::PackingElement::Meta; + +# XXX stuff that's invisible to find_extractible should be considered extracted +# for the most part, otherwise we create broken partial packages +sub find_extractible +{ + my ($self, $state, $wanted, $tied) = @_; + $state->{partial}{$self} = 1; +} + package OpenBSD::PackingElement::ExtraInfo; use OpenBSD::Error; @@ -360,6 +401,18 @@ use File::Basename; use File::Path; use OpenBSD::Temp; +sub find_extractible +{ + my ($self, $state, $wanted, $tied) = @_; + $wanted->{$self->name} = $self; + return; + if ($self->{tied} || $self->{link} || $self->{symlink}) { + $tied->{$self->name} = $self; + } else { + $wanted->{$self->name} = $self; + } +} + sub prepare_for_addition { my ($self, $state, $pkgname) = @_; @@ -384,19 +437,11 @@ sub prepare_for_addition sub prepare_to_extract { - my ($self, $state) = @_; + my ($self, $state, $file) = @_; my $fullname = $self->fullname; my $destdir = $state->{destdir}; - my $file=$state->{archive}->next; - if (!defined $file) { - $state->fatal("truncated archive"); - } $file->{cwd} = $self->cwd; - if (!$file->check_name($self)) { - $state->fatal("archive does not match #1 != #2", - $file->name, $self->name); - } if (defined $self->{symlink} || $file->isSymLink) { unless (defined $self->{symlink} && $file->isSymLink) { $state->fatal("bogus symlink #1", $self->name); @@ -427,14 +472,11 @@ sub prepare_to_extract if (defined $self->{symlink} && $state->{do_faked}) { $file->{linkname} = $destdir.$file->{linkname}; } - return $file; } sub extract { - my ($self, $state) = @_; - - my $file = $self->prepare_to_extract($state); + my ($self, $state, $file) = @_; if (defined $self->{link} || defined $self->{symlink}) { $state->{archive}->skip; @@ -483,6 +525,7 @@ sub extract $state->say("extracting #1", $tempname) if $state->verbose >= 3; + if (!$file->isFile) { $state->fatal("can't extract #1, it's not a file", $self->stringize); @@ -558,6 +601,10 @@ sub prepare_for_addition if ($s->avail < 0) { $s->report_overflow($state, $fname); } +} + +sub find_extractible +{ } sub extract Index: OpenBSD/ProgressMeter.pm =================================================================== RCS file: /build/data/openbsd/cvs/src/usr.sbin/pkg_add/OpenBSD/ProgressMeter.pm,v retrieving revision 1.40 diff -u -p -r1.40 ProgressMeter.pm --- OpenBSD/ProgressMeter.pm 23 Dec 2013 15:04:37 -0000 1.40 +++ OpenBSD/ProgressMeter.pm 13 Jun 2014 17:13:21 -0000 @@ -47,6 +47,15 @@ sub errprint print STDERR @_; } +sub new_sizer +{ + return shift; +} + +sub advance +{ +} + sub for_list { my ($self, $msg, $l, $code) = @_; Index: OpenBSD/ProgressMeter/Term.pm =================================================================== RCS file: /build/data/openbsd/cvs/src/usr.sbin/pkg_add/OpenBSD/ProgressMeter/Term.pm,v retrieving revision 1.23 diff -u -p -r1.23 Term.pm --- OpenBSD/ProgressMeter/Term.pm 18 Mar 2014 18:53:29 -0000 1.23 +++ OpenBSD/ProgressMeter/Term.pm 13 Jun 2014 17:13:21 -0000 @@ -20,11 +20,8 @@ use warnings; package OpenBSD::PackingElement; sub size_and { - my ($self, $progress, $donesize, $totsize, $method, @r) = @_; - if (defined $self->{size}) { - $$donesize += $self->{size}; - $progress->show($$donesize, $totsize); - } + my ($self, $p, $method, @r) = @_; + $p->advance($self); $self->$method(@r); } @@ -80,18 +77,28 @@ sub compute_count sub visit_with_size { my ($progress, $plist, $method, $state, @r) = @_; + my $p = $progress->new_sizer($plist, $state); + $plist->size_and($p, $method, $state, @r); +} + +sub new_sizer +{ + my ($progress, $plist, $state) = @_; $plist->{totsize} //= compute_size($plist); - my $donesize = 0; - my $totsize = $plist->{totsize}; - $progress->show($donesize, $totsize); + my $p = bless { + progress => $progress, + totsize => $plist->{totsize}, + donesize => 0, + state => $state + }, "_SizeCounter"; + $progress->show(0, $p->{totsize}); if (defined $state->{archive}) { $state->{archive}{callback} = sub { my $done = shift; - $progress->show($donesize + $done, $totsize); + $progress->show($p->{donesize} + $done, $p->{totsize}); }; } - $plist->size_and($progress, \$donesize, $totsize, - $method, $state, @r); + return $p; } sub visit_with_count @@ -304,6 +311,20 @@ sub next $todo //= 'ok'; print "\r$self->{header}: $todo\n"; +} + +package _SizeCounter; +sub new +{ +} + +sub advance +{ + my ($self, $e) = @_; + if (defined $e->{size}) { + $self->{donesize} += $e->{size}; + $self->{progress}->show($self->{donesize}, $self->{totsize}); + } } 1;