PackageLocator.pm revision 1.43
1# ex:ts=8 sw=4:
2# $OpenBSD: PackageLocator.pm,v 1.43 2005/10/22 13:11:55 espie Exp $
3#
4# Copyright (c) 2003-2004 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::PackageRepository;
22
23sub _new
24{
25	my ($class, $address) = @_;
26	bless { baseurl => $address }, $class;
27}
28
29sub new
30{
31	my ($class, $baseurl) = @_;
32	if ($baseurl =~ m/^ftp\:/i) {
33		return OpenBSD::PackageRepository::FTP->_new($baseurl);
34	} elsif ($baseurl =~ m/^http\:/i) {
35		return OpenBSD::PackageRepository::HTTP->_new($baseurl);
36	} elsif ($baseurl =~ m/^scp\:/i) {
37		return OpenBSD::PackageRepository::SCP->_new($baseurl);
38	} elsif ($baseurl =~ m/src\:/i) {
39		return OpenBSD::PackageRepository::Source->_new($baseurl);
40	} else {
41		return OpenBSD::PackageRepository::Local->_new($baseurl);
42	}
43}
44
45sub available
46{
47	my $self = shift;
48
49	return @{$self->list()};
50}
51
52sub wipe_info
53{
54	my ($self, $pkg) = @_;
55
56	require File::Path;
57
58	my $dir = $pkg->{dir};
59	if (defined $dir) {
60
61	    File::Path::rmtree($dir);
62	    delete $pkg->{dir};
63	}
64}
65
66# by default, all objects may exist
67sub may_exist
68{
69	return 1;
70}
71
72# by default, we don't track opened files for this key
73
74sub opened
75{
76	undef;
77}
78
79sub close
80{
81	my ($self, $object) = @_;
82	close($object->{fh}) if defined $object->{fh};
83	$self->parse_problems($object->{errors}) if defined $object->{errors};
84	$object->deref();
85}
86
87sub make_room
88{
89	my $self = shift;
90
91	# kill old files if too many
92	my $already = $self->opened();
93	if (defined $already) {
94		# gc old objects
95		if (@$already >= $self->maxcount()) {
96			@$already = grep { defined $_->{fh} } @$already;
97		}
98		while (@$already >= $self->maxcount()) {
99			my $o = shift @$already;
100			$self->close($o);
101		}
102	}
103	return $already;
104}
105
106# open method that tracks opened files per-host.
107sub open
108{
109	my ($self, $object) = @_;
110
111	return undef unless $self->may_exist($object->{name});
112
113	# kill old files if too many
114	my $already = $self->make_room();
115	my $fh = $self->open_pipe($object);
116	if (!defined $fh) {
117		return undef;
118	}
119	$object->{fh} = $fh;
120	if (defined $already) {
121		push @$already, $object;
122	}
123	return $fh;
124}
125
126sub find
127{
128	my ($repository, $name, $arch, $srcpath) = @_;
129	$name.=".tgz" unless $name =~ m/\.tgz$/;
130	my $self = OpenBSD::PackageLocation->new($repository, $name);
131
132	return $self->openPackage($name, $arch);
133}
134
135sub grabPlist
136{
137	my ($repository, $name, $arch, $code) = @_;
138	$name.=".tgz" unless $name =~ m/\.tgz$/;
139	my $self = OpenBSD::PackageLocation->new($repository, $name);
140
141	return $self->grabPlist($name, $arch, $code);
142}
143
144sub parse_problems
145{
146	my ($self, $filename) = @_;
147	CORE::open(my $fh, '<', $filename);
148	my $baseurl = $self->{baseurl};
149	local $_;
150	while(<$fh>) {
151		next if m/^(?:200|220|230|227|250|331|500|150)[\s\-]/;
152		next if m/^EPSV command not understood/;
153		next if m/^Trying [\d\.\:]+\.\.\./;
154		next if m/^Requesting \Q$baseurl\E/;
155		next if m/^Remote system type is\s+/;
156		next if m/^Connected to\s+/;
157		next if m/^remote\:\s+/;
158		next if m/^Using binary mode to transfer files/;
159		next if m/^Retrieving\s+/;
160		print STDERR "Error from $baseurl:\n", $_;
161	}
162	CORE::close($fh);
163	unlink $filename;
164}
165
166package OpenBSD::PackageRepository::Installed;
167our @ISA=qw(OpenBSD::PackageRepository);
168use OpenBSD::PackageInfo;
169
170sub new
171{
172	bless {}, shift;
173}
174
175sub find
176{
177	my ($repository, $name, $arch, $srcpath) = @_;
178	my $self;
179
180	if (is_installed($name)) {
181		$self = OpenBSD::PackageLocation->new($repository, $name);
182		$self->{dir} = installed_info($name);
183	}
184	return $self;
185}
186
187sub grabPlist
188{
189	my ($repository, $name, $arch, $code) = @_;
190	require OpenBSD::PackingList;
191	return  OpenBSD::PackingList->from_installation($name, $code);
192}
193
194sub available
195{
196	return installed_packages();
197}
198
199sub list
200{
201	my @list = installed_packages();
202	return \@list;
203}
204
205sub wipe_info
206{
207}
208
209sub may_exist
210{
211	my ($self, $name) = @_;
212	return is_installed($name);
213}
214
215package PackageRepository::Source;
216
217sub find
218{
219	my ($repository, $name, $arch, $srcpath) = @_;
220	my $dir;
221	my $make;
222	if (defined $ENV{'MAKE'}) {
223		$make = $ENV{'MAKE'};
224	} else {
225		$make = '/usr/bin/make';
226	}
227	if (defined $repository->{baseurl} && $repository->{baseurl} ne '') {
228		$dir = $repository->{baseurl}
229	} elsif (defined $ENV{PORTSDIR}) {
230		$dir = $ENV{PORTSDIR};
231	} else {
232		$dir = '/usr/ports';
233	}
234	# figure out the repository name and the pkgname
235	my $pkgfile = `cd $dir && SUBDIR=$srcpath ECHO_MSG=: $make show=PKGFILE`;
236	chomp $pkgfile;
237	if (! -f $pkgfile) {
238		system "cd $dir && SUBDIR=$srcpath $make package BULK=Yes";
239	}
240	if (! -f $pkgfile) {
241		return undef;
242	}
243	$pkgfile =~ m|(.*/)([^/]*)|;
244	my ($base, $fname) = ($1, $2);
245
246	my $repo = OpenBSD::PackageRepository::Local->_new($base);
247	return $repo->find($fname);
248}
249
250package OpenBSD::PackageRepository::Local;
251our @ISA=qw(OpenBSD::PackageRepository);
252
253sub open_pipe
254{
255	my ($self, $object) = @_;
256	my $pid = open(my $fh, "-|");
257	if (!defined $pid) {
258		die "Cannot fork: $!";
259	}
260	if ($pid) {
261		return $fh;
262	} else {
263		open STDERR, ">/dev/null";
264		exec {"/usr/bin/gzip"}
265		    "gzip",
266		    "-d",
267		    "-c",
268		    "-q",
269		    "-f",
270		    $self->{baseurl}.$object->{name}
271		or die "Can't run gzip";
272	}
273}
274
275sub may_exist
276{
277	my ($self, $name) = @_;
278	return -r $self->{baseurl}.$name;
279}
280
281sub list
282{
283	my $self = shift;
284	my $l = [];
285	my $dname = $self->{baseurl};
286	opendir(my $dir, $dname) or return $l;
287	while (my $e = readdir $dir) {
288		next unless $e =~ m/\.tgz$/;
289		next unless -f "$dname/$e";
290		push(@$l, $`);
291	}
292	close($dir);
293	return $l;
294}
295
296package OpenBSD::PackageRepository::Local::Pipe;
297our @ISA=qw(OpenBSD::PackageRepository::Local);
298
299sub may_exist
300{
301	return 1;
302}
303
304sub open_pipe
305{
306	my ($self, $object) = @_;
307	my $fullname = $self->{baseurl}.$object->{name};
308	my $pid = open(my $fh, "-|");
309	if (!defined $pid) {
310		die "Cannot fork: $!";
311	}
312	if ($pid) {
313		return $fh;
314	} else {
315		open STDERR, ">/dev/null";
316		exec {"/usr/bin/gzip"}
317		    "gzip",
318		    "-d",
319		    "-c",
320		    "-q",
321		    "-f",
322		    "-"
323		or die "can't run gzip";
324	}
325}
326
327package OpenBSD::PackageRepository::Distant;
328our @ISA=qw(OpenBSD::PackageRepository);
329
330my $buffsize = 2 * 1024 * 1024;
331
332sub pkg_copy
333{
334	my ($in, $dir, $name) = @_;
335
336	require File::Temp;
337	my $template = $name;
338	$template =~ s/\.tgz$/.XXXXXXXX/;
339
340	my ($copy, $filename) = File::Temp::tempfile($template,
341	    DIR => $dir) or die "Can't write copy to cache";
342	chmod 0644, $filename;
343	my $handler = sub {
344		my ($sig) = @_;
345		unlink $filename;
346		$SIG{$sig} = 'DEFAULT';
347		kill $sig, $$;
348	};
349
350	{
351
352	local $SIG{'PIPE'} =  $handler;
353	local $SIG{'INT'} =  $handler;
354	local $SIG{'HUP'} =  $handler;
355	local $SIG{'QUIT'} =  $handler;
356	local $SIG{'KILL'} =  $handler;
357	local $SIG{'TERM'} =  $handler;
358
359	my ($buffer, $n);
360	# copy stuff over
361	do {
362		$n = sysread($in, $buffer, $buffsize);
363		if (!defined $n) {
364			die "Error reading\n";
365		}
366		syswrite $copy, $buffer;
367		syswrite STDOUT, $buffer;
368	} while ($n != 0);
369	close($copy);
370	}
371
372	rename $filename, "$dir/$name";
373}
374
375sub open_pipe
376{
377	require OpenBSD::Temp;
378
379	my ($self, $object) = @_;
380	$object->{errors} = OpenBSD::Temp::file();
381	my $pid = open(my $fh, "-|");
382	if (!defined $pid) {
383		die "Cannot fork: $!";
384	}
385	if ($pid) {
386		return $fh;
387	} else {
388		open STDERR, '>', $object->{errors};
389
390		my $pid2 = open(STDIN, "-|");
391
392		if (!defined $pid2) {
393			die "Cannot fork: $!";
394		}
395		if ($pid2) {
396			exec {"/usr/bin/gzip"}
397			    "gzip",
398			    "-d",
399			    "-c",
400			    "-q",
401			    "-"
402			or die "can't run gzip";
403		} else {
404			if (defined $ENV{'PKG_CACHE'}) {
405				my $pid3 = open(my $in, "-|");
406				if (!defined $pid3) {
407					die "Cannot fork: $!";
408				}
409				if ($pid3) {
410					pkg_copy($in, $ENV{'PKG_CACHE'},
411					    $object->{name});
412					exit(0);
413				} else {
414					$self->grab_object($object);
415				}
416			} else {
417				$self->grab_object($object);
418			}
419		}
420	}
421}
422
423sub _list
424{
425	my ($self, $cmd) = @_;
426	my $l =[];
427	local $_;
428	open(my $fh, '-|', "$cmd") or return undef;
429	while(<$fh>) {
430		chomp;
431		next if m/^d.*\s+\S/;
432		next unless m/([^\s]+)\.tgz\s*$/;
433		push(@$l, $1);
434	}
435	close($fh);
436	return $l;
437}
438
439
440package OpenBSD::PackageRepository::SCP;
441our @ISA=qw(OpenBSD::PackageRepository::Distant);
442
443
444sub grab_object
445{
446	my ($self, $object) = @_;
447
448	exec {"/usr/bin/scp"}
449	    "scp",
450	    $self->{host}.":".$self->{path}.$object->{name},
451	    "/dev/stdout"
452	or die "can't run scp";
453}
454
455our %distant = ();
456
457sub maxcount
458{
459	return 2;
460}
461
462sub opened
463{
464	my $self = $_[0];
465	my $k = $self->{key};
466	if (!defined $distant{$k}) {
467		$distant{$k} = [];
468	}
469	return $distant{$k};
470}
471
472sub _new
473{
474	my ($class, $baseurl) = @_;
475	$baseurl =~ s/scp\:\/\///i;
476	$baseurl =~ m/\//;
477	bless {	host => $`, key => $`, path => "/$'" }, $class;
478}
479
480sub list
481{
482	my ($self) = @_;
483	if (!defined $self->{list}) {
484		my $host = $self->{host};
485		my $path = $self->{path};
486		$self->{list} = $self->_list("ssh $host ls -l $path");
487	}
488	return $self->{list};
489}
490
491package OpenBSD::PackageRepository::HTTPorFTP;
492our @ISA=qw(OpenBSD::PackageRepository::Distant);
493
494our %distant = ();
495
496
497sub grab_object
498{
499	my ($self, $object) = @_;
500	my $ftp = defined $ENV{'FETCH_CMD'} ? $ENV{'FETCH_CMD'} : "/usr/bin/ftp";
501	exec {$ftp}
502	    "ftp",
503	    "-o",
504	    "-", $self->{baseurl}.$object->{name}
505	or die "can't run ftp";
506}
507
508sub maxcount
509{
510	return 1;
511}
512
513sub opened
514{
515	my $self = $_[0];
516	my $k = $self->{key};
517	if (!defined $distant{$k}) {
518		$distant{$k} = [];
519	}
520	return $distant{$k};
521}
522
523sub _new
524{
525	my ($class, $baseurl) = @_;
526	my $distant_host;
527	if ($baseurl =~ m/^(http|ftp)\:\/\/(.*?)\//i) {
528	    $distant_host = $&;
529	}
530	bless { baseurl => $baseurl, key => $distant_host }, $class;
531}
532
533
534package OpenBSD::PackageRepository::HTTP;
535our @ISA=qw(OpenBSD::PackageRepository::HTTPorFTP);
536
537sub list
538{
539	my ($self) = @_;
540	if (!defined $self->{list}) {
541		my $error = OpenBSD::Temp::file();
542		$self->make_room();
543		my $fullname = $self->{baseurl};
544		my $l = $self->{list} = [];
545		local $_;
546		open(my $fh, '-|', "ftp -o - $fullname 2>$error") or return undef;
547		# XXX assumes a pkg HREF won't cross a line. Is this the case ?
548		while(<$fh>) {
549			chomp;
550			for my $pkg (m/\<A\s+HREF=\"(.*?)\.tgz\"\>/gi) {
551				next if $pkg =~ m|/|;
552				push(@$l, $pkg);
553			}
554		}
555		close($fh);
556		$self->parse_problems($error);
557	}
558	return $self->{list};
559}
560
561package OpenBSD::PackageRepository::FTP;
562our @ISA=qw(OpenBSD::PackageRepository::HTTPorFTP);
563
564
565sub list
566{
567	my ($self) = @_;
568	if (!defined $self->{list}) {
569		require OpenBSD::Temp;
570
571		my $error = OpenBSD::Temp::file();
572		$self->make_room();
573		my $fullname = $self->{baseurl};
574		$self->{list} = $self->_list("echo 'nlist *.tgz'|ftp -o - $fullname 2>$error");
575		$self->parse_problems($error);
576	}
577	return $self->{list};
578}
579
580package OpenBSD::PackageLocation;
581
582use OpenBSD::PackageInfo;
583use OpenBSD::Temp;
584
585sub new
586{
587	my ($class, $repository, $name) = @_;
588	my $self = { repository => $repository, name => $name};
589	bless $self, $class;
590}
591
592sub openArchive
593{
594	my $self = shift;
595
596	my $fh = $self->{repository}->open($self);
597	if (!defined $fh) {
598		$self->{repository}->parse_problems($self->{errors})
599		    if defined $self->{errors};
600		return undef;
601	}
602	require OpenBSD::Ustar;
603
604	my $archive = new OpenBSD::Ustar $fh;
605	$self->{_archive} = $archive;
606}
607
608sub grabInfoFiles
609{
610	my $self = shift;
611	my $dir = $self->{dir} = OpenBSD::Temp::dir();
612
613	if (defined $self->{contents} && ! -f $dir.CONTENTS) {
614		open my $fh, '>', $dir.CONTENTS or die "Permission denied";
615		print $fh $self->{contents};
616		close $fh;
617	}
618
619	while (my $e = $self->intNext()) {
620		if ($e->isFile() && is_info_name($e->{name})) {
621			$e->{name}=$dir.$e->{name};
622			eval { $e->create(); };
623			if ($@) {
624				unlink($e->{name});
625				$@ =~ s/\s+at.*//;
626				print STDERR $@;
627				return 0;
628			}
629		} else {
630			$self->unput();
631			last;
632		}
633	}
634	return 1;
635}
636
637sub scanPackage
638{
639	my $self = shift;
640	while (my $e = $self->intNext()) {
641		if ($e->isFile() && is_info_name($e->{name})) {
642			if ($e->{name} eq CONTENTS && !defined $self->{dir}) {
643				$self->{contents} = $e->contents();
644				last;
645			}
646			if (!defined $self->{dir}) {
647				$self->{dir} = OpenBSD::Temp::dir();
648			}
649			$e->{name}=$self->{dir}.$e->{name};
650			eval { $e->create(); };
651			if ($@) {
652				unlink($e->{name});
653				$@ =~ s/\s+at.*//;
654				print STDERR $@;
655				return 0;
656			}
657		} else {
658			$self->unput();
659			last;
660		}
661	}
662	return 1;
663}
664
665sub grabPlist
666{
667	my ($self, $pkgname, $arch, $code) = @_;
668
669	my $pkg = $self->openPackage($pkgname, $arch);
670	if (defined $pkg) {
671		my $plist = $self->plist($code);
672		$pkg->wipe_info();
673		$pkg->close();
674		return $plist;
675	} else {
676		return undef;
677	}
678}
679
680sub openPackage
681{
682	my ($self, $pkgname, $arch) = @_;
683	if (!$self->openArchive()) {
684		return undef;
685	}
686	$self->scanPackage();
687
688	if (defined $self->{contents}) {
689		return $self;
690	}
691
692	# maybe it's a fat package.
693	while (my $e = $self->intNext()) {
694		unless ($e->{name} =~ m/\/\+CONTENTS$/) {
695			last;
696		}
697		my $prefix = $`;
698		my $contents = $e->contents();
699		require OpenBSD::PackingList;
700
701		$pkgname =~ s/\.tgz$//;
702
703		my $plist = OpenBSD::PackingList->fromfile(\$contents,
704		    \&OpenBSD::PackingList::FatOnly);
705		next if defined $pkgname and $plist->pkgname() ne $pkgname;
706		if ($plist->has('arch')) {
707			if ($plist->{arch}->check($arch)) {
708				$self->{filter} = $prefix;
709				bless $self, "OpenBSD::FatPackageLocation";
710				$self->{contents} = $contents;
711				return $self;
712			}
713		}
714	}
715	# hopeless
716	$self->close();
717	$self->wipe_info();
718	return undef;
719}
720
721sub wipe_info
722{
723	my $self = shift;
724	$self->{repository}->wipe_info($self);
725}
726
727sub info
728{
729	my $self = shift;
730	if (!defined $self->{dir}) {
731		$self->grabInfoFiles();
732	}
733	return $self->{dir};
734}
735
736sub plist
737{
738	my ($self, $code) = @_;
739
740	require OpenBSD::PackingList;
741
742	if (defined $self->{contents}) {
743		my $value = $self->{contents};
744		return OpenBSD::PackingList->fromfile(\$value, $code);
745	} elsif (defined $self->{dir} && -f $self->{dir}.CONTENTS) {
746		return OpenBSD::PackingList->fromfile($self->{dir}.CONTENTS,
747		    $code);
748	}
749	# hopeless
750	$self->close();
751
752	return undef;
753}
754
755sub close
756{
757	my $self = shift;
758	$self->{repository}->close($self);
759}
760
761sub deref
762{
763	my $self = shift;
764	$self->{fh} = undef;
765	$self->{_archive} = undef;
766}
767
768sub reopen
769{
770	my $self = shift;
771	if (!$self->openArchive()) {
772		return undef;
773	}
774	while (my $e = $self->{_archive}->next()) {
775		if ($e->{name} eq $self->{_current}->{name}) {
776			$self->{_current} = $e;
777			return $self;
778		}
779	}
780	return undef;
781}
782
783# proxy for archive operations
784sub next
785{
786	my $self = shift;
787
788	if (!defined $self->{dir}) {
789		$self->grabInfoFiles();
790	}
791	return $self->intNext();
792}
793
794sub intNext
795{
796	my $self = shift;
797
798	if (!defined $self->{fh}) {
799		if (!$self->reopen()) {
800			return undef;
801		}
802	}
803	if (!$self->{_unput}) {
804		$self->{_current} = $self->getNext();
805	}
806	$self->{_unput} = 0;
807	return $self->{_current};
808}
809
810sub unput
811{
812	my $self = shift;
813	$self->{_unput} = 1;
814}
815
816sub getNext
817{
818	my $self = shift;
819
820	return $self->{_archive}->next();
821}
822
823package OpenBSD::FatPackageLocation;
824our @ISA=qw(OpenBSD::PackageLocation);
825
826sub getNext
827{
828	my $self = shift;
829
830	my $e = $self->SUPER::getNext();
831	if ($e->{name} =~ m/^(.*?)\/(.*)$/) {
832		my ($beg, $name) = ($1, $2);
833		if (index($beg, $self->{filter}) == -1) {
834			return $self->next();
835		}
836		$e->{name} = $name;
837		if ($e->isHardLink()) {
838			$e->{linkname} =~ s/^(.*?)\///;
839		}
840	}
841	return $e;
842}
843
844package OpenBSD::PackageRepositoryList;
845
846sub new
847{
848	my $class = shift;
849	return bless {list => [], avail => undef }, $class;
850}
851
852sub add
853{
854	my $self = shift;
855	push @{$self->{list}}, @_;
856	if (@_ > 0) {
857		$self->{avail} = undef;
858	}
859}
860
861sub find
862{
863	my ($self, $pkgname, $arch, $srcpath) = @_;
864
865	for my $repo (@{$self->{list}}) {
866		my $pkg = $repo->find($pkgname, $arch, $srcpath);
867		return $pkg if defined $pkg;
868	}
869	return undef;
870}
871
872sub grabPlist
873{
874	my ($self, $pkgname, $arch, $code) = @_;
875
876	for my $repo (@{$self->{list}}) {
877		my $plist = $repo->grabPlist($pkgname, $arch, $code);
878		return $plist if defined $plist;
879	}
880	return undef;
881}
882
883sub available
884{
885	my $self = shift;
886
887	if (!defined $self->{avail}) {
888		my $available_packages = {};
889		foreach my $loc (reverse @{$self->{list}}) {
890		    foreach my $pkg (@{$loc->list()}) {
891		    	$available_packages->{$pkg} = $loc;
892		    }
893		}
894		$self->{avail} = $available_packages;
895	}
896	return keys %{$self->{avail}};
897}
898
899package OpenBSD::PackageLocator;
900
901# this returns an archive handle from an uninstalled package name, currently
902# There is a cache available.
903
904my %packages;
905my $pkgpath = OpenBSD::PackageRepositoryList->new();
906
907if (defined $ENV{PKG_PATH}) {
908	my $v = $ENV{PKG_PATH};
909	$v =~ s/^\:+//;
910	$v =~ s/\:+$//;
911	my @tentative = split /\/\:/, $v;
912	while (my $i = shift @tentative) {
913		$i =~ m|/$| or $i.='/';
914		$pkgpath->add(OpenBSD::PackageRepository->new($i));
915	}
916} else {
917	$pkgpath->add(OpenBSD::PackageRepository->new("./"));
918}
919
920sub find
921{
922	my $class = shift;
923	local $_ = shift;
924	my $arch = shift;
925	my $srcpath = shift;
926
927	if ($_ eq '-') {
928		my $repository = OpenBSD::PackageRepository::Local::Pipe->_new('./');
929		my $package = $repository->find(undef, $arch, $srcpath);
930		return $package;
931	}
932	if (exists $packages{$_}) {
933		return $packages{$_};
934	}
935	my $package;
936	if (m/\//) {
937		use File::Basename;
938
939		my ($pkgname, $path) = fileparse($_);
940		my $repository = OpenBSD::PackageRepository->new($path);
941		$package = $repository->find($pkgname, $arch, $srcpath);
942		if (defined $package) {
943			$pkgpath->add($repository);
944		}
945	} else {
946		$package = $pkgpath->find($_, $arch, $srcpath);
947	}
948	$packages{$_} = $package if defined($package);
949	return $package;
950}
951
952sub available
953{
954	return $pkgpath->available();
955}
956
957sub grabPlist
958{
959	my $class = shift;
960	local $_ = shift;
961	my $arch = shift;
962	my $code = shift;
963
964	if ($_ eq '-') {
965		my $repository = OpenBSD::PackageRepository::Local::Pipe->_new('./');
966		my $plist = $repository->grabPlist(undef, $arch, $code);
967		return $plist;
968	}
969	my $plist;
970	if (m/\//) {
971		use File::Basename;
972
973		my ($pkgname, $path) = fileparse($_);
974		my $repository = OpenBSD::PackageRepository->new($path);
975		$plist = $repository->grabPlist($pkgname, $arch, $code);
976		if (defined $plist) {
977			$pkgpath->add($repository);
978		}
979	} else {
980		$plist = $pkgpath->grabPlist($_, $arch, $code);
981	}
982	return $plist;
983}
984
9851;
986