# ex:ts=8 sw=4: # $OpenBSD: PackageLocation.pm,v 1.4 2006/03/13 16:08:12 espie Exp $ # # Copyright (c) 2003-2004 Marc Espie # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. use strict; use warnings; package OpenBSD::PackageLocation; use OpenBSD::PackageInfo; use OpenBSD::Temp; sub new { my ($class, $repository, $name, $arch) = @_; if (defined $name) { $name =~ s/\.tgz$//; } my $self = { repository => $repository, name => $name, arch => $arch}; bless $self, $class; } sub openArchive { my $self = shift; my $fh = $self->{repository}->open($self); if (!defined $fh) { $self->{repository}->parse_problems($self->{errors}) if defined $self->{errors}; undef $self->{errors}; return undef; } require OpenBSD::Ustar; my $archive = new OpenBSD::Ustar $fh; $self->{_archive} = $archive; } sub grabInfoFiles { my $self = shift; my $dir = $self->{dir} = OpenBSD::Temp::dir(); if (defined $self->{contents} && ! -f $dir.CONTENTS) { open my $fh, '>', $dir.CONTENTS or die "Permission denied"; print $fh $self->{contents}; close $fh; } while (my $e = $self->intNext()) { if ($e->isFile() && is_info_name($e->{name})) { $e->{name}=$dir.$e->{name}; eval { $e->create(); }; if ($@) { unlink($e->{name}); $@ =~ s/\s+at.*//; print STDERR $@; return 0; } } else { $self->unput(); last; } } return 1; } sub scanPackage { my $self = shift; while (my $e = $self->intNext()) { if ($e->isFile() && is_info_name($e->{name})) { if ($e->{name} eq CONTENTS && !defined $self->{dir}) { $self->{contents} = $e->contents(); last; } if (!defined $self->{dir}) { $self->{dir} = OpenBSD::Temp::dir(); } $e->{name}=$self->{dir}.$e->{name}; eval { $e->create(); }; if ($@) { unlink($e->{name}); $@ =~ s/\s+at.*//; print STDERR $@; return 0; } } else { $self->unput(); last; } } return 1; } sub grabPlist { my ($self, $code) = @_; my $pkg = $self->openPackage(); if (defined $pkg) { my $plist = $self->plist($code); $pkg->wipe_info(); $pkg->close_now(); return $plist; } else { return undef; } } sub openPackage { my $self = shift; my $arch = $self->{arch}; if (!$self->openArchive()) { return undef; } $self->scanPackage(); if (defined $self->{contents}) { return $self; } # maybe it's a fat package. while (my $e = $self->intNext()) { unless ($e->{name} =~ m/\/\+CONTENTS$/) { last; } my $prefix = $`; my $contents = $e->contents(); require OpenBSD::PackingList; my $plist = OpenBSD::PackingList->fromfile(\$contents, \&OpenBSD::PackingList::FatOnly); if (defined $self->{name}) { next if $plist->pkgname() ne $self->{name}; } if ($plist->has('arch')) { if ($plist->{arch}->check($arch)) { $self->{filter} = $prefix; bless $self, "OpenBSD::FatPackageLocation"; $self->{contents} = $contents; return $self; } } } # hopeless $self->close_with_client_error(); $self->wipe_info(); return undef; } sub wipe_info { my $self = shift; $self->{repository}->wipe_info($self); } sub info { my $self = shift; if (!defined $self->{dir}) { $self->grabInfoFiles(); } return $self->{dir}; } sub plist { my ($self, $code) = @_; require OpenBSD::PackingList; if (defined $self->{contents}) { my $value = $self->{contents}; return OpenBSD::PackingList->fromfile(\$value, $code); } elsif (defined $self->{dir} && -f $self->{dir}.CONTENTS) { return OpenBSD::PackingList->fromfile($self->{dir}.CONTENTS, $code); } # hopeless $self->close_with_client_error(); return undef; } sub close { my ($self, $hint) = @_; $self->{repository}->close($self, $hint); } sub finish_and_close { my $self = shift; $self->{repository}->finish_and_close($self); } sub close_now { my $self = shift; $self->{repository}->close_now($self); } sub close_after_error { my $self = shift; $self->{repository}->close_after_error($self); } sub close_with_client_error { my $self = shift; $self->{repository}->close_with_client_error($self); } sub deref { my $self = shift; $self->{fh} = undef; $self->{pid} = undef; $self->{pid2} = undef; $self->{_archive} = undef; } sub reopen { my $self = shift; if (!$self->openArchive()) { return undef; } while (my $e = $self->{_archive}->next()) { if ($e->{name} eq $self->{_current}->{name}) { $self->{_current} = $e; return $self; } } return undef; } # proxy for archive operations sub next { my $self = shift; if (!defined $self->{dir}) { $self->grabInfoFiles(); } return $self->intNext(); } sub intNext { my $self = shift; if (!defined $self->{fh}) { if (!$self->reopen()) { return undef; } } if (!$self->{_unput}) { $self->{_current} = $self->getNext(); } $self->{_unput} = 0; return $self->{_current}; } sub unput { my $self = shift; $self->{_unput} = 1; } sub getNext { my $self = shift; return $self->{_archive}->next(); } sub skip { my $self = shift; return $self->{_archive}->skip(); } package OpenBSD::FatPackageLocation; our @ISA=qw(OpenBSD::PackageLocation); sub getNext { my $self = shift; my $e = $self->SUPER::getNext(); if ($e->{name} =~ m/^(.*?)\/(.*)$/) { my ($beg, $name) = ($1, $2); if (index($beg, $self->{filter}) == -1) { return $self->next(); } $e->{name} = $name; if ($e->isHardLink()) { $e->{linkname} =~ s/^(.*?)\///; } } return $e; } 1;