Here's an expanded version of the patch. So far, ask_list was happy with prompting, but the mirror list is slightly large, so being able to pipe thru more comes in handy.
This means a bit of refactor: we've got state, so we can get the height from a progressmeter (or the stub), and it's reasonably easy to tweak state display to be able to use arbitrary fh... This shows another logic limitation of the current code, namely that the size detection of the display is linked to the progressmeter options, whereas it should more or less always happen when we detect we got a tty connected, since the width/height information is uncorrelated to whether or not we need/want a progressmeter. To be continued... Index: OpenBSD/AddCreateDelete.pm =================================================================== RCS file: /cvs/src/usr.sbin/pkg_add/OpenBSD/AddCreateDelete.pm,v retrieving revision 1.37 diff -u -p -r1.37 AddCreateDelete.pm --- OpenBSD/AddCreateDelete.pm 15 Jun 2016 15:40:13 -0000 1.37 +++ OpenBSD/AddCreateDelete.pm 22 Jun 2016 15:55:29 -0000 @@ -43,6 +43,11 @@ sub progress return $self->{progressmeter}; } +sub height +{ + my $self = shift; + return $self->{progressmeter}->height; +} sub not { my $self = shift; Index: OpenBSD/Interactive.pm =================================================================== RCS file: /cvs/src/usr.sbin/pkg_add/OpenBSD/Interactive.pm,v retrieving revision 1.20 diff -u -p -r1.20 Interactive.pm --- OpenBSD/Interactive.pm 30 Jan 2015 11:42:55 -0000 1.20 +++ OpenBSD/Interactive.pm 22 Jun 2016 15:55:29 -0000 @@ -35,13 +35,23 @@ sub ask_list if ($self->{always}) { return $values[0]; } + my ($fh, $pid); + if ($self->{state}->height <= @values + 1) { + $pid = open($fh, "|-", "more", "-c"); + } - $self->{state}->errsay('#1', $prompt); + $fh //= \*STDERR; + + $self->{state}->fhsay($fh, '#1', $prompt); my $i = 0; for my $v (@values) { - $self->{state}->errsay("#1\t#2: #3", + $self->{state}->fhsay($fh, "#1\t#2: #3", $i == 0 ? "a" : "", $i, $v); $i++; + } + if (defined $pid) { + close($fh); + waitpid $pid, 0; } LOOP: $self->{state}->errprint("Your choice: "); Index: OpenBSD/PackageLocator.pm =================================================================== RCS file: /cvs/src/usr.sbin/pkg_add/OpenBSD/PackageLocator.pm,v retrieving revision 1.105 diff -u -p -r1.105 PackageLocator.pm --- OpenBSD/PackageLocator.pm 30 Jan 2016 11:29:29 -0000 1.105 +++ OpenBSD/PackageLocator.pm 22 Jun 2016 15:55:29 -0000 @@ -24,6 +24,7 @@ use OpenBSD::PackageRepositoryList; use OpenBSD::PackageRepository; my $default_path; +my $is_configured; sub build_default_path { @@ -37,17 +38,93 @@ sub build_default_path while (my $o = OpenBSD::PackageRepository->parse(\$v, $state)) { $default_path->add($o); } + $is_configured = 1; return; } $default_path->add(OpenBSD::PackageRepository->new("./", $state)->can_be_empty); - return if $state->defines('NOINSTALLPATH'); + if ($state->defines('NOINSTALLPATH')) { + $is_configured = 1; + return; + } return unless defined $state->config->value('installpath'); + $is_configured = 1; for my $i ($state->config->value("installpath")) { $default_path->add(OpenBSD::PackageRepository->new($i, $state)); } } +sub discover_mirror +{ + my ($self, $state) = @_; + + # can't ask the user -> no mirror + return undef unless $state->is_interactive; + + + require OpenBSD::PackageRepository; + # ftp.openbsd.org == 129.128.5.191 and will remain at + # that address for the foreseeable future. + my $fake = OpenBSD::PackageRepository->new("http://129.128.5.191/cgi-bin/", $state); + # XXX + bless $fake, "OpenBSD::PackageRepository::Cgi"; + my $l = $fake->list; + my @m = @$l; + my %h; + for my $d (@m) { + my $e = $d; + $d =~ s,^http://(.*?)(/.*?)?\s+(.*)$,$1\t$3,; + $e =~ s/\s+.*$//; + $h{$d} = $e; + } + $m[0] = "<None>"; + my $i = $state->ask_list("No mirror configured, choose one", @m); + if ($i eq "<None>") { + return undef; + } + return $h{$i}; +} + +sub convert_to_packages +{ + my ($self, $url) = @_; + # mirror was "designed" for base releases. + # convert into short installpath version + $url =~ s,^http://(.*)/pub/OpenBSD$,$1, or + $url =~ s,$,/%c/packages/%a,; + return $url; +} + +sub last_chance +{ + if ($is_configured) { + return []; + } + $is_configured = 1; + my ($self, @search) = @_; + my $state = pop @search; + + my $url = $self->discover_mirror($state); + if (!defined $url) { + return []; + } + + $url = $self->convert_to_packages($url); + + # try setting it "permanently" + if (open(my $f, ">>", OpenBSD::Paths->pkgconf)) { + print $f "installpath += $url\n"; + close $f; + } else { + $state->errsay("Couldn't write to #1", OpenBSD::Paths->pkgconf); + } + + # use it for the current round anyway + $default_path->add(OpenBSD::PackageRepository->new($url, $state)); + + return $self->match_locations(@search, $state); +} + sub default_path { if (!defined $default_path) { @@ -107,4 +184,27 @@ sub match_locations return $self->default_path($state)->match_locations(@search); } +package OpenBSD::PackageRepository::Cgi; +our @ISA = qw(OpenBSD::PackageRepository::HTTP); + +# we know how to get a list, we just need to override the specific url +# and parser +sub get_http_list +{ + my ($self, $error) = @_; + + require OpenBSD::Paths; + my $fullname = $self->url."ftplist.cgi?path=".OpenBSD::Paths->os_directory."/".OpenBSD::Paths->machine_architecture; + my $l = []; + my $fh = $self->open_read_ftp(OpenBSD::Paths->ftp." -o - $fullname", + $error) or return; + while(<$fh>) { + chomp; + if (m/^http:\/\//) { + push(@$l, $_); + } + } + $self->close_read_ftp($fh); + return $l; +} 1; Index: OpenBSD/PackageRepositoryList.pm =================================================================== RCS file: /cvs/src/usr.sbin/pkg_add/OpenBSD/PackageRepositoryList.pm,v retrieving revision 1.30 diff -u -p -r1.30 PackageRepositoryList.pm --- OpenBSD/PackageRepositoryList.pm 9 Jul 2015 12:57:55 -0000 1.30 +++ OpenBSD/PackageRepositoryList.pm 22 Jun 2016 15:55:29 -0000 @@ -86,7 +86,7 @@ sub match_locations return $l; } } - return []; + return $self->{state}->repo->last_chance(@search); } 1; Index: OpenBSD/ProgressMeter.pm =================================================================== RCS file: /cvs/src/usr.sbin/pkg_add/OpenBSD/ProgressMeter.pm,v retrieving revision 1.48 diff -u -p -r1.48 ProgressMeter.pm --- OpenBSD/ProgressMeter.pm 15 Jun 2016 15:31:09 -0000 1.48 +++ OpenBSD/ProgressMeter.pm 22 Jun 2016 15:55:30 -0000 @@ -103,6 +103,10 @@ sub ntogo return ""; } +sub height +{ + return 24; +} sub visit_with_size { my ($progress, $plist, $method, @r) = @_; Index: OpenBSD/State.pm =================================================================== RCS file: /cvs/src/usr.sbin/pkg_add/OpenBSD/State.pm,v retrieving revision 1.34 diff -u -p -r1.34 State.pm --- OpenBSD/State.pm 6 Apr 2015 11:07:24 -0000 1.34 +++ OpenBSD/State.pm 22 Jun 2016 15:55:30 -0000 @@ -137,6 +137,14 @@ sub match_locations return OpenBSD::PackageLocator->match_locations(@_, $self->{state}); } +sub last_chance +{ + my $self = shift; + require OpenBSD::PackageLocator; + + return OpenBSD::PackageLocator->last_chance(@_, $self->{state}); +} + sub grabPlist { my ($self, $url, $code) = @_; @@ -257,50 +265,59 @@ sub fatal $self->_fatal($self->f(@_)); } -sub _print +sub _fhprint { my $self = shift; + my $fh = shift; $self->sync_display; - print @_; + print $fh @_; +} + +sub _print +{ + my $self = shift; + $self->_fhprint(\*STDOUT, @_); } sub _errprint { my $self = shift; - $self->sync_display; - print STDERR @_; + $self->_fhprint(\*STDERR, @_); } sub print { my $self = shift; - $self->_print($self->f(@_)); + $self->_fhprint(\*STDOUT, $self->f(@_)); } -sub say +sub errprint { my $self = shift; + $self->_fhprint(\*STDERR, $self->f(@_)); +} + +sub fhsay +{ + my $self = shift; + my $fh = shift; if (@_ == 0) { - $self->_print("\n"); + $self->_fhprint($fh, "\n"); } else { - $self->_print($self->f(@_), "\n"); + $self->_fhprint($fh, $self->f(@_), "\n"); } } -sub errprint +sub say { my $self = shift; - $self->_errprint($self->f(@_)); + $self->fhsay(\*STDOUT, @_); } sub errsay { my $self = shift; - if (@_ == 0) { - $self->_errprint("\n"); - } else { - $self->_errprint($self->f(@_), "\n"); - } + $self->fhsay(\*STDERR, @_); } sub do_options @@ -340,6 +357,11 @@ sub handle_options } local $Exporter::ExportLevel = $state->{export_level}; import OpenBSD::State; +} + +sub is_interactive +{ + return 0; } sub defines Index: OpenBSD/ProgressMeter/Term.pm =================================================================== RCS file: /cvs/src/usr.sbin/pkg_add/OpenBSD/ProgressMeter/Term.pm,v retrieving revision 1.36 diff -u -p -r1.36 Term.pm --- OpenBSD/ProgressMeter/Term.pm 15 Jun 2016 15:31:09 -0000 1.36 +++ OpenBSD/ProgressMeter/Term.pm 22 Jun 2016 15:55:30 -0000 @@ -127,9 +127,17 @@ sub find_window_size my @l = Term::ReadKey::GetTermSizeGWINSZ(\*STDOUT); if (@l != 4) { $self->{width} = 80; + $self->{height} = 24; } else { $self->{width} = $l[0]; + $self->{height} = $l[1]; } +} + +sub height +{ + my $self = shift; + return $self->{height}; } sub compute_playfield