# bsd-lib.pl
# Functions for FreeBSD package management

use POSIX;
chop($system_arch = `uname -m`);
$package_dir = "/var/db/pkg";

# list_packages([package]*)
# Fills the array %packages with a list of all packages
sub list_packages
{
local $i = 0;
local $arg = @_ ? join(" ", @_) : "-a";
open(PKGINFO, "pkg_info -I $arg |");
while(<PKGINFO>) {
	if (/^(\S+)\s+(.*)/) {
		$packages{$i,'name'} = $1;
		$packages{$i,'class'} = "";
		$packages{$i,'desc'} = $2;
		$i++;
		}
	}
close(PKGINFO);
return $i;
}

# package_info(package)
# Returns an array of package information in the order
#  name, class, description, arch, version, vendor, installtime
sub package_info
{
local $out = `pkg_info $_[0] 2>&1`;
return () if ($?);
local @rv = ( $_[0] );
push(@rv, "");
push(@rv, $out =~ /Description:\n([\0-\177]*\S)/i ? $1 : $text{'bsd_unknown'});
push(@rv, $system_arch);
push(@rv, $_[0] =~ /-([^\-]+)$/ ? $1 : $text{'bsd_unknown'});
push(@rv, "FreeBSD");
local @st = stat("$package_dir/$_[0]");
push(@rv, @st ? ctime($st[9]) : $text{'bsd_unknown'});
return @rv;
}

# check_files(package)
# Fills in the %files array with information about the files belonging
# to some package. Values in %files are  path type user group mode size error
sub check_files
{
local $i = 0;
local $file;
open(PKGINFO, "pkg_info -L $_[0] |");
while($file = <PKGINFO>) {
	$file =~ s/\r|\n//g;
	next if ($file !~ /^\//);
	local @st = stat($file);
	$files{$i,'path'} = $file;
	$files{$i,'type'} = -l $file ? 3 :
			    -d $file ? 1 : 0;
	$files{$i,'user'} = getpwuid($st[4]);
	$files{$i,'group'} = getgrgid($st[5]);
	$files{$i,'mode'} = sprintf "%o", $st[2] & 07777;
	$files{$i,'size'} = $st[7];
	$files{$i,'link'} = readlink($file);
	$i++;
	}
return $i;
}

# installed_file(file)
# Given a filename, fills %file with details of the given file and returns 1.
# If the file is not known to the package system, returns 0
# Usable values in %file are  path type user group mode size packages
sub installed_file
{
local (%packages, $file, $i, @pkgin);
local $n = &list_packages();
for($i=0; $i<$n; $i++) {
	open(PKGINFO, "pkg_info -L $packages{$i,'name'} |");
	while($file = <PKGINFO>) {
		$file =~ s/\r|\n//g;
		if ($file eq $_[0]) {
			# found it
			push(@pkgin, $packages{$i,'name'});
			}
		}
	close(PKGINFO);
	}
if (@pkgin) {
	local @st = stat($_[0]);
	$file{'path'} = $_[0];
	$file{'type'} = -l $_[0] ? 3 :
			-d $_[0] ? 1 : 0;
	$file{'user'} = getpwuid($st[4]);
	$file{'group'} = getgrgid($st[5]);
	$file{'mode'} = sprintf "%o", $st[2] & 07777;
	$file{'size'} = $st[7];
	$file{'link'} = readlink($_[0]);
	$file{'packages'} = join(" ", @pkgin);
	return 1;
	}
else {
	return 0;
	}
}

# is_package(file)
sub is_package
{
local ($desc, $contents);
open(TAR, "gunzip -c $_[0] | tar tf - |");
while(<TAR>) {
	$desc++ if (/^\+DESC/);
	$contents++ if (/^\+CONTENTS/);
	}
close(TAR);
return $desc && $contents;
}

# file_packages(file)
# Returns a list of all packages in the given file, in the form
#  package description
sub file_packages
{
local $temp = &tempname();
mkdir($temp, 0700);
system("cd $temp ; gunzip -c $_[0] | tar xf - +CONTENTS +COMMENT >/dev/null 2>&1");
local ($comment, $name);
open(COMMENT, "$temp/+COMMENT");
($comment = <COMMENT>) =~ s/\r|\n//g;
close(COMMENT);
open(CONTENTS, "$temp/+CONTENTS");
while(<CONTENTS>) {
	$name = $1 if (/^\@name\s+(\S+)/);
	}
close(CONTENTS);
unlink("$temp/+COMMENT", "$temp/+CONTENTS");
rmdir($temp);
return ( "$name $comment" );
}

# install_options(file, package)
# Outputs HTML for choosing install options
sub install_options
{
print "<tr> <td><b>$text{'bsd_scripts'}</b></td>\n";
print "<td><input type=radio name=scripts value=0 checked> $text{'yes'}\n";
print "<input type=radio name=scripts value=1> $text{'no'}</td> </tr>\n";

print "<tr> <td><b>$text{'bsd_force'}</b></td>\n";
print "<td><input type=radio name=force value=1> $text{'yes'}\n";
print "<input type=radio name=force value=0 checked> $text{'no'}</td> </tr>\n";
}

# install_package(file, package)
# Installs the package in the given file, with options from %in
sub install_package
{
local $in = $_[2] ? $_[2] : \%in;
local $args = ($in->{"scripts"} ? " -I" : "").
	      ($in->{"force"} ? " -f" : "");
local $out = &backquote_logged("pkg_add $args $_[0] 2>&1");
if ($?) {
	return "<pre>$out</pre>";
	}
return undef;
}

# delete_package(package)
# Totally remove some package
sub delete_package
{
local $out = &backquote_logged("pkg_delete $_[0] 2>&1");
if ($?) { return "<pre>$out</pre>"; }
return undef;
}

sub package_system
{
return &text('bsd_manager', "FreeBSD");
}

sub package_help
{
return "pkg_add pkg_info pkg_delete";
}

1;
