aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Pipkin <j@dawnrazor.net>2012-08-30 07:20:32 -0500
committerJacob Pipkin <j@dawnrazor.net>2012-08-30 07:20:32 -0500
commit38488004c207508834543e02e991e6129669bc8c (patch)
tree6191765663783a078fc84aa262ed05cc439071df
parentcd16a547b321e8a10716868c7788d016531511d8 (diff)
downloadsbotools2-38488004c207508834543e02e991e6129669bc8c.tar.xz
changes for REQUIRES in SBos for 14, and many cleanups, fixes, enhancements
-rw-r--r--SBO-Lib/lib/SBO/Lib.pm699
-rwxr-xr-xsbocheck17
-rwxr-xr-xsboclean30
-rwxr-xr-xsboconfig63
-rwxr-xr-xsbofind53
-rwxr-xr-xsboinstall14
-rwxr-xr-xsbosnap16
-rwxr-xr-xsboupgrade323
-rw-r--r--t/SBO/Lib.pm668
-rw-r--r--t/SBO/Lib.pm~643
-rwxr-xr-xt/do_tests.sh4
-rwxr-xr-xt/prep.pl46
-rwxr-xr-xt/test.t95
-rwxr-xr-xt/test.t~95
14 files changed, 2190 insertions, 576 deletions
diff --git a/SBO-Lib/lib/SBO/Lib.pm b/SBO-Lib/lib/SBO/Lib.pm
index 7dc19aa..3db3133 100644
--- a/SBO-Lib/lib/SBO/Lib.pm
+++ b/SBO-Lib/lib/SBO/Lib.pm
@@ -2,19 +2,23 @@
#
# vim: set ts=4:noet
#
-# sbolib.sh
+# Lib.pm
# shared functions for the sbo_ scripts.
#
# author: Jacob Pipkin <j@dawnrazor.net>
# date: Setting Orange, the 37th day of Discord in the YOLD 3178
# license: WTFPL <http://sam.zoy.org/wtfpl/COPYING>
-package SBO::Lib 0.7;
-my $version = "0.7";
+use 5.16.0;
+use warnings FATAL => 'all';
+use strict;
+
+package SBO::Lib 1.0;
+my $version = "1.0";
require Exporter;
-@ISA = qw(Exporter);
-@EXPORT = qw(
+our @ISA = qw(Exporter);
+our @EXPORT = qw(
script_error
open_fh
open_read
@@ -29,10 +33,13 @@ require Exporter;
make_distclean
do_upgradepkg
get_sbo_location
+ get_from_info
+ get_tmp_extfn
+ get_tmp_perlfn
);
-use warnings FATAL => 'all';
-use strict;
+$< == 0 or die "This script requires root privileges.\n";
+
use Tie::File;
use Sort::Versions;
use Digest::MD5;
@@ -41,31 +48,30 @@ use File::Path qw(make_path remove_tree);
use Fcntl;
use File::Find;
use File::Temp qw(tempdir tempfile);
-
-$< == 0 or die "This script requires root privileges.\n";
+use Data::Dumper;
+use Fcntl qw(F_SETFD F_GETFD);
our $tempdir = tempdir (CLEANUP => 1);
# subroutine for throwing internal script errors
-sub script_error {
- unless (exists $_[0]) {
- die "A fatal script error has occured. Exiting.\n";
- } else {
- die "A fatal script error has occured:\n$_[0]\nExiting.\n";
- }
+sub script_error (;$) {
+ exists $_[0] ? die "A fatal script error has occured:\n$_[0]\nExiting.\n"
+ : die "A fatal script error has occured. Exiting.\n";
}
# sub for opening files, second arg is like '<','>', etc
-sub open_fh {
- exists $_[1] or script_error ('open_fh requires two arguments');
- script_error ('open_fh first argument not a file') unless -f $_[0];
+sub open_fh ($$) {
+ exists $_[1] or script_error 'open_fh requires two arguments';
+ unless ($_[1] eq '>') {
+ -f $_[0] or script_error 'open_fh first argument not a file';
+ }
my ($file, $op) = @_;
open my $fh, $op, $file or die "Unable to open $file.\n";
return $fh;
}
-sub open_read {
- return open_fh (shift, '<');
+sub open_read ($) {
+ return open_fh shift, '<';
}
# pull in configuration, set sane defaults, etc.
@@ -82,7 +88,7 @@ our %config = (
# if the conf file exists, pull all the $key=$value pairs into a hash
my %conf_values;
if (-f $conf_file) {
- my $fh = open_read ($conf_file);
+ my $fh = open_read $conf_file;
my $text = do {local $/; <$fh>};
%conf_values = $text =~ /^(\w+)=(.*)$/mg;
close $fh;
@@ -91,28 +97,26 @@ if (-f $conf_file) {
for my $key (keys %config) {
$config{$key} = $conf_values{$key} if exists $conf_values{$key};
}
-#$config{$_} = $conf_values{$_} for keys %config;
$config{JOBS} = 'FALSE' unless $config{JOBS} =~ /^\d+$/;
$config{SBO_HOME} = '/usr/sbo' if $config{SBO_HOME} eq 'FALSE';
+# some stuff we'll need later.
my $distfiles = "$config{SBO_HOME}/distfiles";
my $slackbuilds_txt = "$config{SBO_HOME}/SLACKBUILDS.TXT";
-
my $name_regex = '\ASLACKBUILD\s+NAME:\s+';
-sub show_version {
- print "sbotools version $version\n";
- print "licensed under the WTFPL\n";
- print "<http://sam.zoy.org/wtfpl/COPYING>\n";
+sub show_version () {
+ say "sbotools version $version";
+ say 'licensed under the WTFPL';
+ say '<http://sam.zoy.org/wtfpl/COPYING>';
}
# %supported maps what's in /etc/slackware-version to what's at SBo
-sub get_slack_version {
- my %supported = (
- '13.37.0' => '13.37',
- '14.0' => '13.37',
- );
- my $fh = open_read ('/etc/slackware-version');
+# which is now not needed since this version drops support < 14.0
+# but it's already future-proofed, so leave it.
+sub get_slack_version () {
+ my %supported = ('14.0' => '14.0');
+ my $fh = open_read '/etc/slackware-version';
chomp (my $line = <$fh>);
close $fh;
my $version = ($line =~ /\s+(\d+[^\s]+)$/)[0];
@@ -121,13 +125,13 @@ sub get_slack_version {
return $supported{$version};
}
-sub check_slackbuilds_txt {
- return 1 if -f $slackbuilds_txt;
- return;
+# does the SLACKBUILDS.TXT file exist in the sbo tree?
+sub chk_slackbuilds_txt () {
+ return -f $slackbuilds_txt ? 1 : 0;
}
# check for the validity of new $config{SBO_HOME}
-sub check_home {
+sub check_home () {
my $sbo_home = $config{SBO_HOME};
if (-d $sbo_home) {
opendir (my $home_handle, $sbo_home);
@@ -136,62 +140,63 @@ sub check_home {
die "$sbo_home exists and is not empty. Exiting.\n";
}
} else {
- make_path ($sbo_home) or die "Unable to create $sbo_home. Exiting.\n";
+ make_path ($sbo_home) or die "Unable to create $sbo_home.\n";
}
}
-sub rsync_sbo_tree {
- my $slk_version = get_slack_version ();
+# rsync the sbo tree from slackbuilds.org to $config{SBO_HOME}
+sub rsync_sbo_tree () {
+ my $slk_version = get_slack_version;
my @arg = ('rsync', '-a', '--exclude=*.tar.gz', '--exclude=*.tar.gz.asc');
push @arg, "rsync://slackbuilds.org/slackbuilds/$slk_version/*";
- push @arg, $config{SBO_HOME};
- system @arg;
- print "Finished.\n" and return 1;
+ my $out = system @arg, $config{SBO_HOME};
+ say 'Finished.' and return $out;
}
-sub fetch_tree {
- check_home ();
- print "Pulling SlackBuilds tree...\n";
- rsync_sbo_tree (), return 1;
+# wrappers for differing checks and output
+sub fetch_tree () {
+ check_home;
+ say 'Pulling SlackBuilds tree...';
+ rsync_sbo_tree, return $?;
}
-sub update_tree {
- fetch_tree (), return unless check_slackbuilds_txt ();
- print "Updating SlackBuilds tree...\n";
- rsync_sbo_tree (), return 1;
+sub update_tree () {
+ fetch_tree, return unless chk_slackbuilds_txt;
+ say 'Updating SlackBuilds tree...';
+ rsync_sbo_tree, return $?;
}
# if the SLACKBUILDS.TXT is not in $config{SBO_HOME}, we assume the tree has
# not been populated there; prompt the user to automagickally pull the tree.
-sub slackbuilds_or_fetch {
- unless (check_slackbuilds_txt () ) {
- print "It looks like you haven't run \"sbosnap fetch\" yet.\n";
- print "Would you like me to do this now? [y] ";
- <STDIN> =~ /^[Yy\n]/ ? fetch_tree () :
+sub slackbuilds_or_fetch () {
+ unless (chk_slackbuilds_txt) {
+ say 'It looks like you haven\'t run "sbosnap fetch" yet.';
+ print 'Would you like me to do this now? [y] ';
+ <STDIN> =~ /^[Yy\n]/ ? fetch_tree :
die "Please run \"sbosnap fetch\"\n";
}
return 1;
}
# pull an array of hashes, each hash containing the name and version of an sbo
-# currently installed. starting to think it might be better to only pull an
-# array of names, and have another sub to pull the versions.
-sub get_installed_sbos {
+# currently installed.
+sub get_installed_sbos () {
my @installed;
+ # $1 == name, $2 == version
+ my $regex = qr#/([^/]+)-([^-]+)-[^-]+-[^-]+$#;
for my $path (</var/log/packages/*_SBo>) {
- my ($name, $version) =
- ($path =~ qr#/([^/]+)-([^-]+)-[^-]+-[^-]+$#)[0,1];
+ my ($name, $version) = ($path =~ $regex)[0,1];
push @installed, {name => $name, version => $version};
}
- return @installed;
+ return \@installed;
}
# search the SLACKBUILDS.TXT for a given sbo's directory
-sub get_sbo_location {
- exists $_[0] or script_error ('get_sbo_location requires an argument.');
+sub get_sbo_location ($) {
+ exists $_[0] or script_error 'get_sbo_location requires an argument.';
my $sbo = shift;
my $regex = qr#LOCATION:\s+\.(/[^/]+/$sbo)$#;
- my $fh = open_read ($slackbuilds_txt);
+ my $fh = open_read $slackbuilds_txt;
while (my $line = <$fh>) {
if (my $loc = ($line =~ $regex)[0]) {
return "$config{SBO_HOME}$loc";
@@ -200,105 +205,144 @@ sub get_sbo_location {
return;
}
+# pull the sbo name from a $location: $config{SBO_HOME}/system/wine, etc.
+sub get_sbo_from_loc ($) {
+ exists $_[0] or script_error 'get_sbo_from_loc requires an argument.';
+ return (shift =~ qr#/([^/]+)$#)[0];
+}
+
+# pull piece(s) of data, GET, from the $sbo.info file under LOCATION.
+sub get_from_info (%) {
+ my %args = (
+ LOCATION => '',
+ GET => '',
+ @_
+ );
+ unless ($args{LOCATION} && $args{GET}) {
+ script_error 'get_from_info requires LOCATION and GET.';
+ }
+ state $vars = {PRGNAM => ['']};
+ my $sbo = get_sbo_from_loc $args{LOCATION};
+ return $$vars{$args{GET}} if $$vars{PRGNAM}[0] eq $sbo;
+ # if we're here, we haven't read in the .info file yet.
+ my $fh = open_read "$args{LOCATION}/$sbo.info";
+ # suck it all in, clean it all up, stuff it all in $vars.
+ my $contents = do {local $/; <$fh>};
+ $contents =~ s/("|\\\n)//g;
+ $vars = {$contents =~ /^(\w+)=(.*)$/mg};
+ # fill the hash with array refs - even for single values,
+ # since consistency here is a lot easier than sorting it out later
+ for my $key (keys %$vars) {
+ if ($$vars{$key} =~ /\s/) {
+ my @array = split ' ', $$vars{$key};
+ $$vars{$key} = \@array;
+ } else {
+ $$vars{$key} = [$$vars{$key}];
+ }
+ }
+ return exists $$vars{$args{GET}} ? $$vars{$args{GET}} : 0;
+}
+
+# find the version in the tree for a given sbo (provided a location)
+sub get_sbo_version ($) {
+ exists $_[0] or script_error 'get_sbo_version requires an argument.';
+ my $version = get_from_info (LOCATION => shift, GET => 'VERSION');
+ return $$version[0] ? $$version[0] : 0;
+}
+
# for each installed sbo, find out whether or not the version in the tree is
# newer, and compile an array of hashes containing those which are
-sub get_available_updates {
+sub get_available_updates () {
my @updates;
- my @pkg_list = get_installed_sbos ();
- FIRST: for my $key (keys @pkg_list) {
- my $location = get_sbo_location ($pkg_list[$key]{name});
+ my $pkg_list = get_installed_sbos;
+ FIRST: for my $key (keys @$pkg_list) {
+ my $location = get_sbo_location $$pkg_list[$key]{name};
# if we can't find a location, assume invalid and skip
next FIRST unless defined $location;
- my $regex = qr/^VERSION="([^"]+)"/;
- my $fh = open_read ("$location/$pkg_list[$key]{name}.info");
- SECOND: while (my $line = <$fh>) {
- if (my $sbo_version = ($line =~ $regex)[0]) {
- if (versioncmp ($sbo_version, $pkg_list[$key]{version}) == 1) {
- push @updates, {name => $pkg_list[$key]{name},
- installed => $pkg_list[$key]{version},
- update => $sbo_version};
- }
- last SECOND;
- }
+ my $version = get_sbo_version $location;
+ if (versioncmp ($version, $$pkg_list[$key]{version}) == 1) {
+ push @updates, {
+ name => $$pkg_list[$key]{name},
+ installed => $$pkg_list[$key]{version},
+ update => $version
+ };
}
- close $fh;
}
- return @updates;
-}
-
-# pull links or md5sums (type - 'download','md5sum') from a given sbo's .info
-# file, first checking for x86_64-specific info we are told to
-sub find_download_info {
- exists $_[3] or script_error
- ('find_download_info requires four arguments.');
- my ($sbo, $location, $type, $x64) = @_;
- my @return;
- $type =~ tr/a-z/A-Z/;
- $type = $x64 ? "${type}_x86_64" : $type;
- my $regex = qr/$type="([^"\s]*)("|\s)/;
- my $empty_regex = qr/=""$/;
- # may be > 1 lines for a given key.
- my $back_regex = qr/\\$/;
- my $un_regex = qr/^UN(SUPPOR|TES)TED$/;
- my $more = 'FALSE';
- my $fh = open_read ("$location/$sbo.info");
- FIRST: while (my $line = <$fh>) {
- if ($more eq 'FALSE') {
- if ($line =~ $regex) {
- last FIRST if $line =~ $empty_regex;
- # some sbos have UNSUPPORTED for the x86_64 info
- $1 =~ $un_regex ? last FIRST : push @return, $1;
- $more = 'TRUE' if $line =~ $back_regex;
- }
- } else {
- $more = 'FALSE' unless $line =~ $back_regex;
- # we can assume anything we need will be at least 6 chars long
- push @return, ($line =~ /([^\s"]{6,})/)[0];
+ return \@updates;
+}
+
+# get downloads and md5sums from an sbo's .info file, first
+# checking for x86_64-specific info if we are told to
+sub get_download_info (%) {
+ my %args = (
+ LOCATION => 0,
+ X64 => 1,
+ @_
+ );
+ $args{LOCATION} or script_error 'get_download_info requires LOCATION.';
+ my ($get, $downs, $md5s, %return);
+ $get = ($args{X64} ? 'DOWNLOAD_x86_64' : 'DOWNLOAD');
+ $downs = get_from_info (LOCATION => $args{LOCATION}, GET => $get);
+ # did we get nothing back, or UNSUPPORTED/UNTESTED?
+ if ($args{X64}) {
+ my $nothing;
+ if (! $$downs[0]) {
+ $nothing = 1;
+ } elsif ($$downs[0] =~ qr/^UN(SUPPOR|TES)TED$/) {
+ $nothing = 1;
+ }
+ if ($nothing) {
+ $args{X64} = 0;
+ $downs = get_from_info (LOCATION => $args{LOCATION},
+ GET => 'DOWNLOAD');
}
}
- close $fh;
- return @return if exists $return[0];
- return;
+ # if we still don't have any links, something is really wrong.
+ return unless $$downs[0];
+ # grab the md5s and build a hash
+ $get = $args{X64} ? 'MD5SUM_x86_64' : 'MD5SUM';
+ $md5s = get_from_info (LOCATION => $args{LOCATION}, GET => $get);
+ return unless $$md5s[0];
+ $return{$$downs[$_]} = $$md5s[$_] for (keys @$downs);
+ return %return;
}
-sub get_arch {
+sub get_arch () {
chomp (my $arch = `uname -m`);
return $arch;
}
-# assemble an array of hashes containing links and md5sums for a given sbo,
-# with the option of only checking for 32-bit links, for -compat32 packaging
-sub get_sbo_downloads {
- exists $_[2] or script_error
- ('get_sbo_downloads requires three arguments.');
- -d $_[1] or script_error ('get_sbo_downloads given a non-directory.');
- my ($sbo, $location, $only32) = @_;
- my $arch = get_arch ();
- my (@links, @md5s);
+# TODO: should probably combine this with get_download_info
+sub get_sbo_downloads (%) {
+ my %args = (
+ LOCATION => '',
+ 32 => 0,
+ @_
+ );
+ $args{LOCATION} or script_error 'get_sbo_downloads requires LOCATION.';
+ my $location = $args{LOCATION};
+ -d $location or script_error 'get_sbo_downloads given a non-directory.';
+ my $arch = get_arch;
+ my %dl_info;
if ($arch eq 'x86_64') {
- unless ($only32 eq 'TRUE') {
- @links = find_download_info ($sbo, $location, 'download', 1);
- @md5s = find_download_info ($sbo, $location, 'md5sum', 1);
- }
- }
- unless (exists $links[0]) {
- @links = find_download_info ($sbo, $location, 'download', 0);
- @md5s = find_download_info ($sbo, $location, 'md5sum', 0);
+ %dl_info = get_download_info (LOCATION => $location) unless $args{32};
+ }
+ unless (keys %dl_info > 0) {
+ %dl_info = get_download_info (LOCATION => $location, X64 => 0);
}
- my @downloads;
- push @downloads, {link => $links[$_], md5sum => $md5s[$_]} for keys @links;
- return @downloads;
+ return %dl_info;
}
-sub get_filename_from_link {
- exists $_[0] or script_error
- ('get_filename_from_link requires an argument');
+# given a link, grab the filename from the end of it
+sub get_filename_from_link ($) {
+ exists $_[0] or script_error 'get_filename_from_link requires an argument';
return "$distfiles/". (shift =~ qr#/([^/]+)$#)[0];
}
-sub compute_md5sum {
- -f $_[0] or script_error ('compute_md5sum requires a file argument.');
- my $fh = open_read (shift);
+# for a given file, computer its md5sum
+sub compute_md5sum ($) {
+ -f $_[0] or script_error 'compute_md5sum requires a file argument.';
+ my $fh = open_read shift;
my $md5 = Digest::MD5->new;
$md5->addfile ($fh);
my $md5sum = $md5->hexdigest;
@@ -306,82 +350,67 @@ sub compute_md5sum {
return $md5sum;
}
+sub compare_md5s ($$) {
+ exists $_[1] or script_error 'compare_md5s requires two arguments.';
+ my ($first, $second) = @_;
+ return $first eq $second ? 1 : 0;
+}
+
# for a given distfile, see whether or not it exists, and if so, if its md5sum
# matches the sbo's .info file
-sub check_distfile {
- exists $_[1] or script_error ('check_distfile requires two arguments.');
+sub verify_distfile ($$) {
+ exists $_[1] or script_error 'check_distfile requires two arguments.';
my ($link, $info_md5sum) = @_;
- my $filename = get_filename_from_link ($link);
+ my $filename = get_filename_from_link $link;
return unless -d $distfiles;
return unless -f $filename;
- my $md5sum = compute_md5sum ($filename);
- return unless $info_md5sum eq $md5sum;
- return 1;
+ my $md5sum = compute_md5sum $filename;
+ return compare_md5s $info_md5sum, $md5sum;
}
# for a given distfile, attempt to retrieve it and, if successful, check its
# md5sum against that in the sbo's .info file
-sub get_distfile {
- exists $_[1] or script_error ('get_distfile requires an argument');
- my ($link, $expected_md5sum) = @_;
- my $filename = get_filename_from_link ($link);
- mkdir ($distfiles) unless -d $distfiles;
- chdir ($distfiles);
+sub get_distfile ($$) {
+ exists $_[1] or script_error 'get_distfile requires an argument';
+ my ($link, $exp_md5) = @_;
+ my $filename = get_filename_from_link $link;
+ mkdir $distfiles unless -d $distfiles;
+ chdir $distfiles;
system ("wget --no-check-certificate $link") == 0 or
die "Unable to wget $link\n";
- my $md5sum = compute_md5sum ($filename);
- $md5sum eq $expected_md5sum or die "md5sum failure for $filename.\n";
+ my $md5sum = compute_md5sum $filename;
+ # can't do anything if the link in the .info doesn't lead to a good d/l
+ compare_md5s $md5sum, $exp_md5 or die "md5sum failure for $filename.\n";
return 1;
}
-# find the version in the tree for a given sbo
-sub get_sbo_version {
- exists $_[1] or script_error ('get_sbo_version requires two arguments.');
- my ($sbo, $location) = @_;
- my $version;
- my $fh = open_read ("$location/$sbo.info");
- my $version_regex = qr/^VERSION="([^"]+)"/;
- FIRST: while (my $line = <$fh>) {
- last FIRST if $version = ($line =~ $version_regex)[0];
- }
- close $fh;
- return $version;
-}
-
-# for a given distfile, what will be the full path of the symlink?
-sub get_symlink_from_filename {
+# for a given distfile, figure out what the full path to its symlink will be
+sub get_symlink_from_filename ($$) {
exists $_[1] or script_error
- ('get_symlink_from_filename requires two arguments');
+ 'get_symlink_from_filename requires two arguments';
-f $_[0] or script_error
- ('get_symlink_from_filename first argument is not a file');
+ 'get_symlink_from_filename first argument is not a file';
my ($filename, $location) = @_;
- my @split = split ('/', reverse ($filename), 2);
- return "$location/". reverse ($split[0]);
+ return "$location/". ($filename =~ qr#/([^/]+)$#)[0];
}
# determine whether or not a given sbo is 32-bit only
-sub check_x32 {
- exists $_[1] or script_error ('check_x32 requires two arguments.');
- my ($sbo, $location) = @_;
- my $fh = open_read ("$location/$sbo.info");
- my $regex = qr/^DOWNLOAD_x86_64="UN(SUPPOR|TES)TED"/;
- while (my $line = <$fh>) {
- return 1 if $line =~ $regex;
- }
- close $fh;
- return;
+sub check_x32 ($) {
+ exists $_[0] or script_error 'check_x32 requires an argument.';
+ my $dl = get_from_info (LOCATION => shift, GET => 'DOWNLOAD_x86_64');
+ return $$dl[0] =~ /UN(SUPPOR|TES)TED/ ? 1 : 0;
}
# can't do 32-bit on x86_64 without this file, so we'll use it as the test to
# to determine whether or not an x86_64 system is setup for multilib
-sub check_multilib {
+sub check_multilib () {
return 1 if -f '/etc/profile.d/32dev.sh';
return;
}
# make a backup of the existent SlackBuild, and rewrite the original as needed
-sub rewrite_slackbuild {
- exists $_[1] or script_error ('rewrite_slackbuild requires two arguments.');
+sub rewrite_slackbuild ($$%) {
+ exists $_[1] or script_error 'rewrite_slackbuild requires two arguments.';
my ($slackbuild, $tempfn, %changes) = @_;
copy ($slackbuild, "$slackbuild.orig") or
die "Unable to backup $slackbuild to $slackbuild.orig\n";
@@ -389,7 +418,8 @@ sub rewrite_slackbuild {
my $makepkg_regex = qr/makepkg/;
my $libdir_regex = qr/^\s*LIBDIRSUFFIX="64"\s*$/;
my $make_regex = qr/^\s*make(| \Q||\E exit 1)$/;
- my $arch_out_regex = qr/\$VERSION-\$ARCH-\$BUILD/;
+ my $arch_regex = qr/\$VERSION-\$ARCH-\$BUILD/;
+ # tie the slackbuild, because this is the easiest way to handle this.
tie my @sb_file, 'Tie::File', $slackbuild;
for my $line (@sb_file) {
# get the output of the tar and makepkg commands. hope like hell that v
@@ -397,16 +427,15 @@ sub rewrite_slackbuild {
if ($line =~ $tar_regex || $line =~ $makepkg_regex) {
$line = "$line | tee -a $tempfn";
}
- while (my ($key, $value) = each %changes) {
- if ($key eq 'libdirsuffix') {
- $line =~ s/64/$value/ if $line =~ $libdir_regex;
- }
- if ($key eq 'make') {
- $line =~ s/make/make $value/ if $line =~ $make_regex;
- }
- if ($key eq 'arch_out') {
- $line =~ s/\$ARCH/$value/ if $line =~ $arch_out_regex;
- }
+ # then check for and apply any other %changes
+ if (exists $changes{libdirsuffix}) {
+ $line =~ s/64/$changes{libdirsuffix}/ if $line =~ $libdir_regex;
+ }
+ if (exists $changes{make}) {
+ $line =~ s/make/make $changes{make}/ if $line =~ $make_regex;
+ }
+ if (exists $changes{arch_out}) {
+ $line =~ s/\$ARCH/$changes{arch_out}/ if $line =~ $arch_regex;
}
}
untie @sb_file;
@@ -414,8 +443,8 @@ sub rewrite_slackbuild {
}
# move a backed-up .SlackBuild file back into place
-sub revert_slackbuild {
- exists $_[0] or script_error ('revert_slackbuild requires an argument');
+sub revert_slackbuild ($) {
+ exists $_[0] or script_error 'revert_slackbuild requires an argument';
my $slackbuild = shift;
if (-f "$slackbuild.orig") {
unlink $slackbuild if -f $slackbuild;
@@ -424,141 +453,199 @@ sub revert_slackbuild {
return 1;
}
+# for each $download, see if we have it, and if the copy we have is good,
+# otherwise download a new copy
+sub check_distfiles (%) {
+ exists $_[0] or script_error 'check_distfiles requires an argument.';
+ my %dists = @_;
+ for my $link (keys %dists) {
+ my $md5sum = $dists{$link};
+ unless (verify_distfile $link, $md5sum) {
+ die unless get_distfile $link, $md5sum;
+ }
+ }
+ return 1;
+}
+
# given a location and a list of download links, assemble a list of symlinks,
# and create them.
-sub create_symlinks {
- exists $_[1] or script_error ('create_symlinks requires two arguments.');
- my ($location, @downloads) = @_;
+sub create_symlinks ($%) {
+ exists $_[1] or script_error 'create_symlinks requires two arguments.';
+ my ($location, %downloads) = @_;
my @symlinks;
- for my $key (keys @downloads) {
- my $link = $downloads[$key]{link};
- my $md5sum = $downloads[$key]{md5sum};
- my $filename = get_filename_from_link ($link);
- unless (check_distfile ($link, $md5sum) ) {
- die unless get_distfile ($link, $md5sum);
- }
- my $symlink = get_symlink_from_filename ($filename, $location);
+ for my $link (keys %downloads) {
+ my $filename = get_filename_from_link $link;
+ my $symlink = get_symlink_from_filename $filename, $location;
push @symlinks, $symlink;
- symlink ($filename, $symlink);
+ symlink $filename, $symlink;
}
return @symlinks;
}
-# make a .SlackBuild executable.
-sub prep_sbo_file {
- exists $_[1] or script_error ('prep_sbo_file requires two arguments');
- my ($sbo, $location) = @_;
- chdir ($location);
- chmod (0755, "$location/$sbo.SlackBuild");
- return 1;
-}
-
# pull the untarred source directory or created package name from the temp
# file (the one we tee'd to)
-sub grok_temp_file {
- exists $_[1] or script_error ('grok_temp_file requires two arguments');
- my ($tempfn, $find) = @_;
+sub grok_temp_file (%) {
+ my %args = (
+ FH => '',
+ REGEX => '',
+ CAPTURE => 0,
+ @_
+ );
+ unless ($args{FH} && $args{REGEX}) {
+ script_error 'grok_temp_file requires two arguments';
+ }
+ my $fh = $args{FH};
+ seek $fh, 0, 0;
my $out;
- my $pkg_regex = qr/^Slackware\s+package\s+([^\s]+)\s+created\.$/;
- my $src_regex = qr#^([^/]+)/.*$#;
- my $fh = open_read ($tempfn);
FIRST: while (my $line = <$fh>) {
- if ($find eq 'pkg') {
- last FIRST if $out = ($line =~ $pkg_regex)[0];
- } elsif ($find eq 'src') {
- last FIRST if $out = ($line =~ $src_regex)[0];
+ if ($line =~ $args{REGEX}) {
+ $out = ($line =~ $args{REGEX})[$args{CAPTURE}];
+ last FIRST;
}
}
- close $fh;
+# close $fh;
return $out;
}
# wrappers around grok_temp_file
-sub get_src_dir {
- exists $_[0] or script_error ('get_src_dir requires an argument');
- return grok_temp_file (shift, 'src');
+sub get_src_dir ($) {
+ exists $_[0] or script_error 'get_src_dir requires an argument';
+ return grok_temp_file (FH => shift, REGEX => qr#^([^/]+)/#);
+}
+
+sub get_pkg_name ($) {
+ exists $_[0] or script_error 'get_pkg_name requires an argument';
+ return grok_temp_file (FH => shift,
+ REGEX => qr/^Slackware\s+package\s+([^\s]+)\s+created\.$/);
+}
+
+sub clear_coe_bit ($) {
+ exists $_[0] or script_error 'clear_coe_bit requires an argument';
+ my $fh = shift;
+ fcntl ($fh, F_SETFD, 0) or die "no unset exec-close thingy\n";
+ return $fh;
}
-sub get_pkg_name {
- exists $_[0] or script_error ('get_pkg_name requires an argument');
- return grok_temp_file (shift, 'pkg');
+sub get_tmp_extfn ($) {
+ exists $_[0] or script_error 'get_tmp_extfn requires an argument.';
+ my $fh = clear_coe_bit shift;
+ return '/dev/fd/'. fileno $fh;
+}
+
+sub get_tmp_perlfn ($) {
+ exists $_[0] or script_error 'get_tmp_perlfn requires an argument.';
+ my $fh = clear_coe_bit shift;
+ return '+<=&'. fileno $fh;
}
# prep and run .SlackBuild
-sub perform_sbo {
- exists $_[6] or script_error ('perform_sbo requires seven arguments');
- my ($opts, $jobs, $sbo, $location, $arch, $c32, $x32) = @_;
- prep_sbo_file ($sbo, $location);
+sub perform_sbo (%) {
+ my %args = (
+ OPTS => 0,
+ JOBS => 0,
+ LOCATION => '',
+ ARCH => '',
+ C32 => 0,
+ X32 => 0,
+ @_
+ );
+ unless ($args{LOCATION} && $args{ARCH}) {
+ script_error 'perform_sbo requires LOCATION and ARCH.';
+ }
+ my $location = $args{LOCATION};
+ my $sbo = get_sbo_from_loc $location;
my ($cmd, %changes);
- $jobs eq 'FALSE' or $changes{make} = "-j $jobs";
- if ($arch eq 'x86_64' and ($c32 eq 'TRUE' || $x32) ) {
- if ($c32 eq 'TRUE') {
+ # figure out any changes we need to make to the .SlackBuild
+ $changes{make} = "-j $args{JOBS}" if $args{JOBS};
+ if ($args{ARCH} eq 'x86_64' and ($args{C32} || $args{X32})) {
+ if ($args{C32}) {
$changes{libdirsuffix} = '';
- } elsif ($x32) {
+ } elsif ($args{X32}) {
$changes{arch_out} = 'i486';
}
- $cmd = ". /etc/profile.d/32dev.sh && $location/$sbo.SlackBuild";
- } else {
- $cmd = "$location/$sbo.SlackBuild";
+ $cmd = ". /etc/profile.d/32dev.sh &&";
}
- $cmd = "$opts $cmd" unless $opts eq 'FALSE';
- my ($tempfh, $tempfn) = tempfile (DIR => $tempdir);
- close $tempfh;
- rewrite_slackbuild ("$location/$sbo.SlackBuild", $tempfn, %changes);
- my $out = system $cmd;
- revert_slackbuild ("$location/$sbo.SlackBuild");
+ $cmd .= "/bin/sh $location/$sbo.SlackBuild";
+ $cmd = "$args{OPTS} $cmd" if $args{OPTS};
+ my $tempfh = tempfile (DIR => $tempdir);
+ my $fn = get_tmp_extfn $tempfh;
+ rewrite_slackbuild "$location/$sbo.SlackBuild", $fn, %changes;
+ chdir $location, my $out = system $cmd;
+ revert_slackbuild "$location/$sbo.SlackBuild";
die unless $out == 0;
- my $src = get_src_dir ($tempfn);
- my $pkg = get_pkg_name ($tempfn);
- unlink $tempfn;
+ my $pkg = get_pkg_name $tempfh;
+ my $src = get_src_dir $tempfh;
return $pkg, $src;
}
+# run convertpkg on a package to turn it into a -compat32 thing
+sub do_convertpkg ($) {
+ exists $_[0] or script_error 'do_convertpkg requires an argument.';
+ my $pkg = shift;
+ my $tempfh = tempfile (DIR => $tempdir);
+ my $fn = get_tmp_extfn $tempfh;
+ my $cmd = "/usr/sbin/convertpkg-compat32 -i $pkg -d /tmp | tee $fn";
+ system ($cmd) == 0 or die;
+ unlink $pkg;
+ return get_pkg_name $tempfh;
+}
+
# "public interface", sort of thing.
-sub do_slackbuild {
- exists $_[4] or script_error ('do_slackbuild requires five arguments.');
- my ($opts, $jobs, $sbo, $location, $compat32) = @_;
- my $arch = get_arch ();
- my $version = get_sbo_version ($sbo, $location);
- my @downloads = get_sbo_downloads ($sbo, $location, $compat32);
+sub do_slackbuild (%) {
+ my %args = (
+ OPTS => 0,
+ JOBS => 0,
+ LOCATION => '',
+ COMPAT32 => 0,
+ @_
+ );
+ $args{LOCATION} or script_error 'do_slackbuild requires LOCATION.';
+ my $location = $args{LOCATION};
+ my $sbo = get_sbo_from_loc $location;
+ my $arch = get_arch;
+ my $multi = check_multilib;
+ my $version = get_sbo_version $location;
my $x32;
- if ($compat32 eq 'TRUE') {
- unless ($arch eq 'x86_64') {
- die "You can only create compat32 packages on x86_64 systems.\n";
- } else {
- die "This system does not appear to be setup for multilib.\n"
- unless check_multilib ();
- die "compat32 pkgs require /usr/sbin/convertpkg-compat32.\n"
+ # ensure x32 stuff is set correctly, or that we're setup for it
+ if ($args{COMPAT32}) {
+ die "compat32 only works on x86_64.\n" unless $arch eq 'x86_64';
+ die "compat32 requires multilib.\n" unless $multi;
+ die "compat32 requires /usr/sbin/convertpkg-compat32.\n"
unless -f '/usr/sbin/convertpkg-compat32';
- }
} else {
if ($arch eq 'x86_64') {
- $x32 = check_x32 ($sbo, $location);
- if ($x32 && ! check_multilib () ) {
- die "$sbo is 32-bit, but this system does not seem to be setup for multilib.\n";
+ $x32 = check_x32 $args{LOCATION};
+ if ($x32 && ! $multi) {
+ die "$sbo is 32-bit which requires multilib on x86_64.\n";
}
}
}
- my @symlinks = create_symlinks ($location, @downloads);
- my ($pkg, $src) = perform_sbo
- ($opts, $jobs, $sbo, $location, $arch, $compat32, $x32);
- if ($compat32 eq 'TRUE') {
- my ($tempfh, $tempfn) = tempfile (DIR => $tempdir);
- close $tempfh;
- my $cmd = "/usr/sbin/convertpkg-compat32 -i $pkg -d /tmp | tee $tempfn";
- system ($cmd) == 0 or die;
- unlink $pkg;
- $pkg = get_pkg_name ($tempfn);
- }
+ # get a hash of downloads and md5sums, ensure we have 'em, symlink 'em
+ my %downloads = get_sbo_downloads (
+ LOCATION => $location,
+ 32 => $args{COMPAT32}
+ );
+ check_distfiles %downloads;
+ my @symlinks = create_symlinks $args{LOCATION}, %downloads;
+ # setup and run the .SlackBuild itself
+ my ($pkg, $src) = perform_sbo (
+ OPTS => $args{OPTS},
+ JOBS => $args{JOBS},
+ LOCATION => $location,
+ ARCH => $arch,
+ C32 => $args{COMPAT32},
+ X32 => $x32,
+ );
+ do_convertpkg $pkg if $args{COMPAT32};
unlink $_ for @symlinks;
return $version, $pkg, $src;
}
# remove work directories (source and packaging dirs under /tmp/SBo)
-sub make_clean {
- exists $_[1] or script_error ('make_clean requires two arguments.');
+sub make_clean ($$$) {
+ exists $_[2] or script_error 'make_clean requires three arguments.';
my ($sbo, $src, $version) = @_;
- print "Cleaning for $sbo-$version...\n";
+ say "Cleaning for $sbo-$version...";
my $tmpsbo = "/tmp/SBo";
remove_tree ("$tmpsbo/$src") if -d "$tmpsbo/$src";
remove_tree ("$tmpsbo/package-$sbo") if -d "$tmpsbo/package-$sbo";
@@ -566,23 +653,31 @@ sub make_clean {
}
# remove distfiles
-sub make_distclean {
- exists $_[3] or script_error ('make_distclean requires four arguments.');
- my ($sbo, $src, $version, $location) = @_;
- make_clean ($sbo, $src, $version);
- print "Distcleaning for $sbo-$version...\n";
- my @downloads = get_sbo_downloads ($sbo, $location, 0);
- for my $key (keys @downloads) {
- my $filename = get_filename_from_link ($downloads[$key]{link});
+sub make_distclean (%) {
+ my %args = (
+ SRC => '',
+ VERSION => '',
+ LOCATION => '',
+ @_
+ );
+ unless ($args{SRC} && $args{VERSION} && $args{LOCATION}) {
+ script_error 'make_distclean requires four arguments.';
+ }
+ my $sbo = get_sbo_from_loc $args{LOCATION};
+ make_clean $sbo, $args{SRC}, $args{VERSION};
+ say "Distcleaning for $sbo-$args{VERSION}...";
+ # remove any distfiles for this particular SBo.
+ my %downloads = get_sbo_downloads (LOCATION => $args{LOCATION});
+ for my $key (keys %downloads) {
+ my $filename = get_filename_from_link $key;
unlink $filename if -f $filename;
}
return 1;
}
# run upgradepkg for a created package
-sub do_upgradepkg {
- exists $_[0] or script_error ('do_upgradepkg requires an argument.');
+sub do_upgradepkg ($) {
+ exists $_[0] or script_error 'do_upgradepkg requires an argument.';
system ('/sbin/upgradepkg', '--reinstall', '--install-new', shift);
return 1;
}
-
diff --git a/sbocheck b/sbocheck
index 6e748a2..9d228a6 100755
--- a/sbocheck
+++ b/sbocheck
@@ -9,6 +9,7 @@
# date: Sweetmorn, the 38th day of Discord in the YOLD 3178
# license: WTFPL <http://sam.zoy.org/wtfpl/COPYING>
+use 5.16.0;
use SBO::Lib;
use File::Basename;
use Getopt::Std;
@@ -22,18 +23,18 @@ my $self = basename ($0);
my %options;
getopts ('v',\%options);
-show_version () && exit (0) if (exists $options{v});
+show_version && exit 0 if (exists $options{v});
-update_tree ();
+update_tree;
print "Checking for updated SlackBuilds...\n";
-my @updates = get_available_updates ();
+my $updates = get_available_updates;
# pretty formatting.
my @listing;
-for my $key (keys @updates) {
- my $string = "$updates[$key]{name}-$updates[$key]{installed}";
- $string .= " < needs updating (SBo has $updates[$key]{update})\n";
+for my $key (keys @$updates) {
+ my $string = "$$updates[$key]{name}-$$updates[$key]{installed}";
+ $string .= " < needs updating (SBo has $$updates[$key]{update})\n";
push @listing, $string;
}
@@ -41,9 +42,9 @@ if (exists $listing[0]) {
my $tab = new Text::Tabulate ();
$tab->configure (tab => '\s');
my $output = $tab->format (@listing);
- print "\n". $output ."\n";
+ say "\n". $output;
} else {
- print "\nNo updates available.\n";
+ say "\nNo updates available.";
}
exit 0;
diff --git a/sboclean b/sboclean
index 3e4da48..3154bd1 100755
--- a/sboclean
+++ b/sboclean
@@ -9,6 +9,7 @@
# date: Boomtime, the 6th day of Confusion in the YOLD 3178
# license: WTFPL <http://sam.zoy.org/wtfpl/COPYING>
+use 5.16.0;
use SBO::Lib;
use File::Basename;
use Getopt::Std;
@@ -19,7 +20,7 @@ use warnings FATAL => 'all';
my %config = %SBO::Lib::config;
my $self = basename ($0);
-sub show_usage {
+sub show_usage () {
print <<EOF
Usage: $self (options) [package]
@@ -36,26 +37,25 @@ EOF
my %options;
getopts ('hvdwi', \%options);
-show_usage () && exit (0) if exists $options{h};
-show_version () && exit (0) if exists $options{v};
-my $clean_dist = exists $options{d} ? 'TRUE' : 'FALSE';
-my $clean_work = exists $options{w} ? 'TRUE' : 'FALSE';
-my $interactive = exists $options{i} ? 'TRUE' : 'FALSE';
+show_usage && exit 0 if exists $options{h};
+show_version && exit 0 if exists $options{v};
+my $clean_dist = exists $options{d} ? 1 : 0;
+my $clean_work = exists $options{w} ? 1 : 0;
+my $interactive = exists $options{i} ? 1 : 0;
-if ($clean_dist eq 'FALSE' && $clean_work eq 'FALSE') {
- show_usage ();
- die "You must specify at least one of -d or -w.\n";
+unless ($clean_dist || $clean_work) {
+ show_usage, die "You must specify at least one of -d or -w.\n";
}
-sub remove_stuff {
- exists $_[0] or script_error ('remove_stuff requires an argument');
- print "Nothing to do.\n" and return 1 unless -d $_[0];
+sub remove_stuff ($) {
+ exists $_[0] or script_error 'remove_stuff requires an argument';
+ say "Nothing to do." and return 1 unless -d $_[0];
my $dir = shift;
opendir (my $dh, $dir);
FIRST: while (my $ls = readdir $dh) {
next FIRST if $ls =~ /^(\.){1,2}$/;
my $full = "$dir/$ls";
- if ($interactive eq 'TRUE') {
+ if ($interactive) {
print "Remove $full? [n] ";
next FIRST unless <STDIN> =~ /^[Yy]/;
}
@@ -64,7 +64,7 @@ sub remove_stuff {
}
}
-remove_stuff ($config{SBO_HOME} . '/distfiles') if $clean_dist eq 'TRUE';
-remove_stuff ('/tmp/SBo') if $clean_work eq 'TRUE';
+remove_stuff $config{SBO_HOME} .'/distfiles' if $clean_dist;
+remove_stuff '/tmp/SBo' if $clean_work;
exit 0;
diff --git a/sboconfig b/sboconfig
index 829d7a3..b5c1d6d 100755
--- a/sboconfig
+++ b/sboconfig
@@ -9,6 +9,7 @@
# date: Pungenday, the 40th day of Discord in the YOLD 3178
# license: WTFPL <http://sam.zoy.org/wtfpl/COPYING>
+use 5.16.0;
use strict;
use warnings FATAL => 'all';
use SBO::Lib;
@@ -21,7 +22,7 @@ use File::Temp qw(tempfile);;
my %config = %SBO::Lib::config;
my $self = basename ($0);
-sub show_usage {
+sub show_usage () {
print <<EOF
Usage: $self [options] [arguments]
@@ -48,16 +49,8 @@ EOF
my %options;
getopts ('hvlc:d:p:s:j:', \%options);
-show_usage () and exit (0) if exists $options{h};
-show_version () and exit (0) if exists $options{v};
-
-if (exists $options{l}) {
- my @keys = sort {$a cmp $b} keys %config;
- print "$_=$config{$_}\n" for @keys;
- exit 0;
-}
-
-show_usage () and exit (0) unless %options;
+show_usage and exit 0 if exists $options{h};
+show_version and exit 0 if exists $options{v};
my %valid_confs = (
c => 'NOCLEAN',
@@ -66,7 +59,17 @@ my %valid_confs = (
p => 'PKG_DIR',
s => 'SBO_HOME',
);
-
+
+my %params = reverse %valid_confs;
+
+if (exists $options{l}) {
+ my @keys = sort {$a cmp $b} keys %config;
+ say "sboconfig -$params{$_}:\n $_=$config{$_}" for @keys;
+ exit 0;
+}
+
+show_usage and exit 0 unless %options;
+
# setup what's being changed.
my %changes;
while (my ($key, $value) = each %valid_confs) {
@@ -77,52 +80,48 @@ if (exists $changes{JOBS}) {
($changes{JOBS} =~ /^\d+$/ || $changes{JOBS} eq 'FALSE');
}
-my $conf_dir = $SBO::Lib::conf_dir;;
+my $conf_dir = $SBO::Lib::conf_dir;
my $conf_file = $SBO::Lib::conf_file;
# safely modify our conf file; copy to a temp location, edit the temp file,
# move the edited file into place
-sub config_write {
- exists $_[1] or script_error ('config_write requires two arguments.');
+sub config_write ($$) {
+ exists $_[1] or script_error 'config_write requires two arguments.';
my ($key, $val) = @_;
if (! -d $conf_dir) {
- mkdir ($conf_dir) or die "Unable to create $conf_dir. Exiting.\n";
+ mkdir $conf_dir or die "Unable to create $conf_dir. Exiting.\n";
}
if (-f $conf_file) {
- my ($fh, $filename) = tempfile (DIR => $SBO::Lib::tempdir);
- close $fh;
- copy ($conf_file, $filename);
+ my $tempfh = tempfile (DIR => $SBO::Lib::tempdir);
+ my $tempfn = get_tmp_perlfn $tempfh;
+ copy ($conf_file, $tempfn);
# tie the file so that if $key is already there, we just change that
# line and untie it
- tie my @temp, 'Tie::File', $filename;
- my $has = 'FALSE';
+ tie my @temp, 'Tie::File', $tempfn;
+ my $has = 0;
my $regex = qr/\A\Q$key\E=/;
FIRST: for my $tmpline (@temp) {
- if ($tmpline =~ $regex) {
- $has = 'TRUE';
- $tmpline = "$key=$val";
- last FIRST;
- }
+ $has++, $tmpline = "$key=$val", last FIRST if $tmpline =~ $regex;;
}
untie @temp;
# otherwise, append our new $key=$value pair
- if ($has eq 'FALSE') {
- my $fh = open_fh ($filename, '>>');
+ unless ($has) {
+ my $fh = open_fh ($tempfn, '>>');
print {$fh} "$key=$val\n";
close $fh;
}
- move ($filename, $conf_file);
+ move ($tempfn, $conf_file);
} else {
# no config file, easiest case of all.
- my $fh = open_fh ($conf_file, '>');
- print {$fh} "$key=$val\n";
+ my $fh = open_fh $conf_file, '>';
+ say {$fh} "$key=$val";
close $fh;
}
}
while (my ($key, $value) = each %changes) {
print "Setting $key to $value...\n";
- config_write ($key, $value);
+ config_write $key, $value;
}
exit 0;
diff --git a/sbofind b/sbofind
index 5e8931d..e00f8de 100755
--- a/sbofind
+++ b/sbofind
@@ -9,6 +9,7 @@
# date: Boomtime, the 39th day of Discord in the YOLD 3178
# license: WTFPL <http://sam.zoy.org/wtfpl/COPYING>
+use 5.16.0;
use SBO::Lib;
use File::Basename;
use Getopt::Std;
@@ -18,7 +19,7 @@ use warnings FATAL => 'all';
my %config = %SBO::Lib::config;
my $self = basename ($0);
-sub show_usage {
+sub show_usage () {
print <<EOF
Usage: $self (search_term)
@@ -37,40 +38,40 @@ EOF
my %options;
getopts ('hvir', \%options);
-show_usage () and exit (0) if (exists $options{h});
-show_version () and exit (0) if (exists $options{v});
+show_usage and exit 0 if exists $options{h};
+show_version and exit 0 if exists $options{v};
-my $show_readme = exists $options{r} ? 'TRUE' : 'FALSE';
-my $show_info = exists $options{i} ? 'TRUE' : 'FALSE';
+my $show_readme = exists $options{r} ? 1 : 0;
+my $show_info = exists $options{i} ? 1 : 0;
-show_usage () and exit (1) unless exists $ARGV[0];
+show_usage and exit 1 unless exists $ARGV[0];
my $search = $ARGV[0];
# if we can't find SLACKBUILDS.TXT in $config{HOME}, prompt to fetch the tree
-slackbuilds_or_fetch ();
+slackbuilds_or_fetch;
# find anything with $search in its name
-my (@findings, $name);
-my $found = 'FALSE';
+my ($findings, $name);
+my $found = 0;
my $name_regex = qr/NAME:\s+(.*\Q$search\E.*)$/i;
my $loc_regex = qr/LOCATION:\s+(.*)$/;
-my $fh = open_read ("$config{SBO_HOME}/SLACKBUILDS.TXT");
+my $fh = open_read "$config{SBO_HOME}/SLACKBUILDS.TXT";
FIRST: while (my $line = <$fh>) {
- if ($found eq 'FALSE') {
- $found = 'TRUE', next FIRST if $name = ($line =~ $name_regex)[0];
+ unless ($found) {
+ $found++, next FIRST if $name = ($line =~ $name_regex)[0];
} else {
if (my ($location) = ($line =~ $loc_regex)[0]) {
- $found = 'FALSE';
+ $found = 0;
$location =~ s#^\.##;
- push @findings, {$name => $config{SBO_HOME} . $location};
+ push @$findings, {$name => $config{SBO_HOME} . $location};
}
}
}
-sub get_file_contents {
- exists $_[0] or script_error ('get_file_contents requires an argument');
- -f $_[0] or script_error ('get_file_contents argument is not a file');
- my $fh = open_read (shift);
+sub get_file_contents ($) {
+ exists $_[0] or script_error 'get_file_contents requires an argument';
+ -f $_[0] or return "$_[0] doesn't exist.\n";
+ my $fh = open_read shift;
my $contents = do {local $/; <$fh>};
$contents =~ s/\n/\n /g;
$contents =~ s/ $//g;
@@ -78,22 +79,22 @@ sub get_file_contents {
}
# pretty formatting
-if (exists $findings[0]) {
+if (exists $$findings[0]) {
my @listing = ("\n");
- for my $hash (@findings) {
- while (my ($key, $value) = each %{$hash}) {
+ for my $hash (@$findings) {
+ while (my ($key, $value) = each %$hash) {
push @listing, "SBo: $key\n";
push @listing, "Path: $value\n";
- push @listing, "info: ". get_file_contents ("$value/$key.info")
- if $show_info eq 'TRUE';
- push @listing, "README: ". get_file_contents ("$value/README")
- if $show_readme eq 'TRUE';
+ push @listing, "info: ". get_file_contents "$value/$key.info"
+ if $show_info;
+ push @listing, "README: ". get_file_contents "$value/README"
+ if $show_readme;
push @listing, "\n";
}
}
print $_ for @listing;
} else {
- print "Nothing found for search term: $search\n";
+ say "Nothing found for search term: $search";
}
exit 0;
diff --git a/sboinstall b/sboinstall
index 76cfa9f..3637207 100755
--- a/sboinstall
+++ b/sboinstall
@@ -9,6 +9,7 @@
# date: Pungenday, the 40th day of Discord in the YOLD 3178
# license: WTFPL <http://sam.zoy.org/wtfpl/COPYING>
+use 5.16.0;
use SBO::Lib;
use Getopt::Std;
use File::Basename;
@@ -17,7 +18,7 @@ use warnings FATAL => 'all';
my $self = basename ($0);
-sub show_usage {
+sub show_usage () {
print <<EOF
Usage: $self [options] sbo
@@ -38,10 +39,10 @@ EOF
my %options;
getopts ('hvcdripj:R', \%options);
-show_usage () and exit (0) if exists $options{h};
-show_version () and exit (0) if exists $options{v};
+show_usage and exit 0 if exists $options{h};
+show_version and exit 0 if exists $options{v};
-show_usage () and exit (0) unless exists $ARGV[0];
+show_usage and exit 0 unless exists $ARGV[0];
# setup any options which do not require arguments
my @opts1 = ('c', 'd', 'r', 'i', 'p', 'R');
@@ -55,6 +56,7 @@ for my $opt (@opts2) {
unshift @ARGV, "-$opt $options{$opt}" if exists $options{$opt};
}
-unshift @ARGV, '/usr/sbin/sboupgrade', '-oN';
-system @ARGV;
+system '/usr/sbin/sboupgrade', '-oN', @ARGV;
+
exit 0;
+
diff --git a/sbosnap b/sbosnap
index bca0d61..076cfb9 100755
--- a/sbosnap
+++ b/sbosnap
@@ -12,10 +12,10 @@
# changelog:
# .01: initial creation.
+use 5.16.0;
use SBO::Lib;
use File::Basename;
use Getopt::Std;
-use feature switch;
use warnings FATAL => 'all';
use strict;
@@ -23,7 +23,7 @@ my %config = %SBO::Lib::config;
my $sbo_home = $config{SBO_HOME};
my $self = basename ($0);
-sub show_usage {
+sub show_usage () {
print <<EOF
Usage: $self [options|command]
@@ -39,25 +39,25 @@ Commands:
EOF
}
-show_usage () and exit (1) unless exists $ARGV[0];
+show_usage and exit 1 unless exists $ARGV[0];
my %options;
getopts ('hv', \%options);
-show_usage () and exit (0) if exists $options{h};
-show_version () and exit (0) if exists $options{v};
+show_usage and exit 0 if exists $options{h};
+show_version and exit 0 if exists $options{v};
# check for a command and, if found, execute it
my $command;
if ($ARGV[0] =~ /fetch|update/) {
$command = $ARGV[0];
} else {
- show_usage () and exit 1;
+ show_usage and exit 1;
}
given ($command) {
- when ('fetch') { fetch_tree () }
- when ('update') { update_tree () }
+ when ('fetch') {fetch_tree}
+ when ('update') {update_tree}
}
exit 0;
diff --git a/sboupgrade b/sboupgrade
index 2daedb9..cf4468a 100755
--- a/sboupgrade
+++ b/sboupgrade
@@ -9,6 +9,7 @@
# date: Boomtime, the 39th day of Discord in the YOLD 3178
# license: WTFPL <http://sam.zoy.org/wtfpl/COPYING>
+use 5.16.0;
use SBO::Lib;
use File::Basename;
use Getopt::Std;
@@ -19,7 +20,7 @@ use warnings FATAL => 'all';
my %config = %SBO::Lib::config;
my $self = basename ($0);
-sub show_usage {
+sub show_usage () {
print <<EOF
Usage: $self (options) [package]
@@ -45,17 +46,17 @@ EOF
my %options;
getopts ('hvacdfj:NriopR', \%options);
-show_usage () && exit (0) if exists $options{h};
-show_version () && exit (0) if exists $options{v};
+show_usage && exit 0 if exists $options{h};
+show_version && exit 0 if exists $options{v};
my $noclean = exists $options{c} ? 'TRUE' : $config{NOCLEAN};
my $distclean = exists $options{d} ? 'TRUE' : $config{DISTCLEAN};
-my $force = exists $options{f} ? 'TRUE' : 'FALSE';
-my $install_new = exists $options{N} ? 'TRUE' : 'FALSE';
-my $no_readme = exists $options{r} ? 'TRUE' : 'FALSE';
-my $no_install = exists $options{i} ? 'TRUE' : 'FALSE';
-my $only_new = exists $options{o} ? 'TRUE' : 'FALSE';
-my $compat32 = exists $options{p} ? 'TRUE' : 'FALSE';
-my $no_reqs = exists $options{R} ? 'TRUE' : 'FALSE';
+my $force = exists $options{f} ? 1 : 0;
+my $install_new = exists $options{N} ? 1 : 0;
+my $no_readme = exists $options{r} ? 1 : 0;
+my $no_install = exists $options{i} ? 1 : 0;
+my $only_new = exists $options{o} ? 1 : 0;
+my $compat32 = exists $options{p} ? 1 : 0;
+my $no_reqs = exists $options{R} ? 1 : 0;
if (exists $options{j}) {
die "You have provided an invalid parameter for -j\n" unless
@@ -63,103 +64,71 @@ if (exists $options{j}) {
}
my $jobs = exists $options{j} ? $options{j} : $config{JOBS};
-show_usage () and exit (1) unless exists $ARGV[0];
+show_usage and exit 1 unless exists $ARGV[0];
# if we can't find SLACKBUILDS.TXT in $config{HOME}, prompt to fetch the tree
-slackbuilds_or_fetch ();
+slackbuilds_or_fetch;
# build a hash of locations for each item provided on command line, at the same
# time verifying each item is a valid slackbuild
my %locations;
for my $sbo_name (@ARGV) {
- $locations{$sbo_name} = get_sbo_location ($sbo_name);
+ $locations{$sbo_name} = get_sbo_location $sbo_name;
die "Unable to locate $sbo_name in the SlackBuilds.org tree.\n" unless
defined $locations{$sbo_name};
}
-sub get_readme_path {
- exists $_[0] or script_error ('get_readme_path requires an argument.');
+sub get_readme_path ($) {
+ exists $_[0] or script_error 'get_readme_path requires an argument.';
my $sbo = shift;
return $locations{$sbo} .'/README';
}
-# this subroutine may be getting a little out of hand.
-sub grok_requirements {
- exists $_[1] or script_error ('grok_requirements requires two arguments');
- return if $no_reqs eq 'TRUE';
- my ($sbo, $readme) = @_;
- my $readme_orig = $readme;
- for ($readme) {
- # deal with and at end of line
- s/and/and /g;
- # work around missing period at end of list of requirements (given 2
- # \ns), or no period at end of whole thing.
- s/$/./;
- # yet another nasty hack. yanh!
- s/[Oo]ptional/./g;
- s/\n\n/./g;
- s/\n//g;
- }
- return unless my $string =
- ($readme =~ /([Tt]his|\Q$sbo\E|)\s*[Rr]equire(s|)(|:)\s+([^\.]+)/)[3];
- for ($string) {
- # remove anything in brackets or parens
- s/(\s)*\[[^\]]+\](\s)*//g;
- s/(\s)*\([^\)]+\)(\s)*//g;
- # convert and to comma
- s/(\s+|,)and\s+/,/g;
- s/,\s+/,/g;
- }
- my @deps = split /,/, $string;
- # if anything has a space, we didn't parse correctly, so remove it, also
- # remove anything that's blank or has an equal sign in
- my @remove;
- for my $key (keys @deps) {
- push @remove, $key if ($deps[$key] =~ /[\s=]/ || $deps[$key] =~ /^$/);
- }
- for my $rem (@remove) {
- splice @deps, $rem, 1;
- $_-- for @remove;
- }
- return unless exists $deps[0];
- FIRST: for my $need (@deps) {
- # compare against installed slackbuilds
- my $tempname = $compat32 eq 'TRUE' ? "$need-compat32" : $need;
- my @inst = get_installed_sbos ();
- SECOND: for my $key (keys @inst) {
- next FIRST if $tempname eq $inst[$key]{name};
- }
- print "\n". $readme_orig;
- print "\nIt looks like this slackbuild requires $tempname; shall I";
- print " attempt to install it first? [y] ";
- if (<STDIN> =~ /^[Yy\n]/) {
- my @args = ("/usr/sbin/sboupgrade", '-oN');
- # populate args so that they carry over correctly
- push @args, "-c" if exists $options{c};
- push @args, "-d" if exists $options{d};
- push @args, "-j $options{j}" if exists $options{j};
- push @args, "-p" if $compat32 eq 'TRUE';
- push @args, $need;
- system (@args) == 0 or
- die "Requirement failure, unable to proceed.\n";
+# for a ref to an array of hashes of installed packages, return an array ref
+# consisting of just their names
+sub get_inst_names ($) {
+ exists $_[0] or script_error 'get_inst_names requires an argument.';
+ my $inst = shift;
+ my $installed;
+ push @$installed, $$_{name} for @$inst;
+ return $installed;
+}
+
+# pull list of requirements, offer to install them
+sub grok_requirements ($$$) {
+ exists $_[1] or script_error 'grok_requirements requires an argument.';
+ my ($sbo, $location, $readme) = @_;
+ my $requires = get_from_info (LOCATION => $location, GET => 'REQUIRES');
+ return unless $$requires[0];
+ for my $req (@$requires) {
+ my $inst = get_installed_sbos;
+ my $inst_names= get_inst_names $inst;;
+ unless ($req ~~ @$inst_names) {
+ say $readme;
+ say "$sbo has $req listed as a requirement.";
+ print "Shall I attempt to install it first? [y] ";
+ if (<STDIN> =~ /^[Yy\n]/) {
+ my @cmd = ('/usr/sbin/sboupgrade', '-oN', $req);
+ system (@cmd) == 0 or die "$req failed to install.\n";
+ }
}
}
- return;
+ return 1;
}
# look for any (user|group)add commands in the README
-sub grok_user_group {
- exists $_[0] or script_error ('grok_user_group requires an argument');
+sub grok_user_group ($) {
+ exists $_[0] or script_error 'grok_user_group requires an argument';
my $readme = shift;
- my @readme_array = split /\n/, $readme;
- my @cmds;
+ my $readme_array = [split /\n/, $readme];
my $cmd_regex = qr/^\s*#\s+((user|group)add.*)/;
- push @cmds, ($_ =~ $cmd_regex)[0] for @readme_array;
+ my @cmds;
+ push @cmds, ($_ =~ $cmd_regex)[0] for @$readme_array;
return unless exists $cmds[0];
- print "\n". $readme ."\n";;
+ say "\n". $readme;
print "\nIt looks like this slackbuild requires the following command(s)";
- print " to be run first:\n";
- print " # $_\n" for @cmds;
+ say " to be run first:";
+ say " # $_" for @cmds;
print "Shall I run it/them now? [y] ";
if (<STDIN> =~ /^[Yy\n]/) {
for my $cmd (@cmds) {
@@ -170,73 +139,81 @@ sub grok_user_group {
}
# see if the README mentions any options
-sub grok_options {
- exists $_[0] or script_error ('grok_options requires an argument');
+sub grok_options ($) {
+ exists $_[0] or script_error 'grok_options requires an argument';
my $readme = shift;
- return 7 unless $readme =~ /[A-Z]+=[^\s]/;
- my @readme_array = split /\n/, $readme;
- print "\n". $readme;
+ return unless $readme =~ /[A-Z]+=[^\s]/;
+ say "\n". $readme;
print "\nIt looks this slackbuilds has options; would you like to set any";
print " when the slackbuild is run? [n] ";
if (<STDIN> =~ /^[Yy]/) {
- my $ask = sub {
+ my $ask = sub () {
print "\nPlease supply any options here, or enter to skip: ";
chomp (my $opts = <STDIN>);
- return 7 if $opts =~ /^$/;
- return $opts; };
+ return if $opts =~ /^$/;
+ return $opts;
+ };
my $kv_regex = qr/[A-Z]+=[^\s]+(|\s([A-Z]+=[^\s]+){0,})/;
- my $opts = &$ask ();
+ my $opts = &$ask;
FIRST: while ($opts !~ $kv_regex) {
warn "Invalid input received.\n";
- $opts = &$ask ();
- return 7 if $opts eq "7";
+ $opts = &$ask;
}
return $opts;
}
- return 7;
+ return;
}
-# prompt for the readme, and grok the readme at this time also.
-sub readme_prompt {
- exists $_[0] or script_error ('readme_prompt requires an argument.');
- my $sbo = shift;
- my $fh = open_read (get_readme_path ($sbo) );
+# prompt for the readme
+sub readme_prompt ($$) {
+ exists $_[0] or script_error 'readme_prompt requires an argument.';
+ my ($sbo, $location) = @_;
+ my $fh = open_read (get_readme_path $sbo);
my $readme = do {local $/; <$fh>};
close $fh;
- grok_requirements ($sbo, $readme);
- grok_user_group ($readme);
- my $opts = grok_options ($readme);
- print "\n". $readme if ($opts eq "7" || ! $opts);
- my $name = $compat32 eq 'TRUE' ? "$sbo-compat32" : $sbo;
+ # check for requirements, useradd/groupadd, options
+ grok_requirements $sbo, $location, $readme;
+ grok_user_group $readme;
+ my $opts = grok_options $readme;
+ print "\n". $readme unless $opts;
+ # present the name as -compat32 if appropriate
+ my $name = $compat32 ? "$sbo-compat32" : $sbo;
print "\nProceed with $name? [y]: ";
exit 0 unless <STDIN> =~ /^[Yy\n]/;
- return $opts if defined $opts;
- return 1;
+ return $opts;
}
# do the things with the provided sbos - whether upgrades or new installs.
sub process_sbos {
- exists $_[0] or script_error ('process_sbos requires an argument.');
- my @todo = @_;
+ exists $_[0] or script_error 'process_sbos requires an argument.';
+ my $todo = shift;
my @failures;
- FIRST: for my $sbo (@todo) {
+ FIRST: for my $sbo (keys %$todo) {
my $opts = 0;
- $opts = readme_prompt ($sbo) unless $no_readme eq 'TRUE';
- $opts = 'FALSE' if ($opts =~ /\d+/ || ! $opts);
+ $opts = readme_prompt $sbo, $$todo{$sbo} unless $no_readme;
# switch compat32 on if upgrading a -compat32
- $compat32 = 'TRUE' if $sbo =~ /-compat32$/;
+ $compat32 = 1 if $sbo =~ /-compat32$/;
my ($version, $pkg, $src);
- my @sb_args = ($opts, $jobs, $sbo, $locations{$sbo}, $compat32);
- eval { ($version, $pkg, $src) = do_slackbuild (@sb_args); };
+ eval { ($version, $pkg, $src) = do_slackbuild (
+ OPTS => $opts,
+ JOBS => $jobs,
+ LOCATION => $locations{$sbo},
+ COMPAT32 => $compat32,
+ ); };
if ($@) {
push @failures, $sbo;
} else {
unless ($distclean eq 'TRUE') {
- make_clean ($sbo, $src, $version) if $noclean eq 'FALSE';
+ make_clean $sbo, $src, $version unless $noclean eq 'TRUE';
} else {
- make_distclean ($sbo, $src, $version, $locations{$sbo});
+ make_distclean (
+ SBO => $sbo,
+ SRC => $src,
+ VERSION => $version,
+ LOCATION => $locations{$sbo},
+ );
}
- do_upgradepkg ($pkg) unless $no_install eq 'TRUE';
+ do_upgradepkg $pkg unless $no_install;
# move package to $config{PKG_DIR} if defined
unless ($config{PKG_DIR} eq 'FALSE') {
my $dir = $config{PKG_DIR};
@@ -244,7 +221,7 @@ sub process_sbos {
mkdir ($dir) or warn "Unable to create $dir\n";
}
if (-d $dir) {
- move ($pkg, $dir), print "$pkg stored in $dir\n";
+ move ($pkg, $dir), say "$pkg stored in $dir";
} else {
warn "$pkg left in /tmp\n";
}
@@ -256,80 +233,68 @@ sub process_sbos {
return @failures;
}
-my @failed;
-
-sub print_failures {
- if (exists $failed[0]) {
- print "Failures:\n";
- print " $_\n" for @failed;
+sub print_failures (;@) {
+ if (exists $_[0]) {
+ say "Failures:";
+ say " $_" for @_;
exit 1;
}
}
# deal with any updates prior to any new installs.
# no reason to bother if only_new is specified, ie running from sboinstall.
-unless ($only_new eq 'TRUE') {
- # doesn't matter what's updatable and what's not if force is specified
- my @updates unless $force eq 'TRUE';
- unless ($force eq 'TRUE') {
- my @updates_array = get_available_updates ();
- push @updates, $updates_array[$_]{name} for keys @updates_array;
+goto INSTALL_NEW if $only_new;
+
+# doesn't matter what's updatable and what's not if force is specified
+my @updates unless $force;
+unless ($force) {
+ my $updates = get_available_updates;
+ push @updates, $$_{name} for @$updates;
+}
+my $todo_upgrade;
+# but without force, we only want to update what there are updates for
+unless ($force) {
+ for my $sbo (@ARGV) {
+ $$todo_upgrade{$sbo} = $locations{$sbo} if $sbo ~~ @updates;
}
- my @todo_upgrade;
- # but without force, we only want to update what there are updates for
- unless ($force eq 'TRUE') {
- for my $sbo (@ARGV) {
- push @todo_upgrade, $sbo if $sbo ~~ @updates;
- }
- } else {
- my @inst = get_installed_sbos ();
- FIRST: for my $sbo (@ARGV) {
- SECOND: for my $key (keys @inst) {
- if ($sbo eq $inst[$key]{name}) {
- push @todo_upgrade, $sbo;
- last SECOND;
- }
- }
+} else {
+ my $inst = get_installed_sbos;
+ my $inst_names= get_inst_names $inst;;
+ FIRST: for my $sbo (@ARGV) {
+ if ($sbo ~~ @$inst_names) {
+ $$todo_upgrade{$sbo} = $locations{$sbo};
}
}
- @failed = process_sbos (@todo_upgrade) if exists $todo_upgrade[0];
- print_failures () unless $install_new eq 'TRUE';
}
+my @failures = process_sbos $todo_upgrade if keys %$todo_upgrade > 0;
+print_failures @failures;
-if ($install_new eq 'TRUE') {
- my @todo_install;
- FIRST: for my $sbo (@ARGV) {
- my $has = 'FALSE';
- my $name = $compat32 eq 'TRUE' ? "$sbo-compat32" : $sbo;
- my @inst = get_installed_sbos ();
- SECOND: for my $key (keys @inst) {
- $has = 'TRUE', last SECOND if $name eq $inst[$key]{name};
- }
- # if compat32 is TRUE, we need to see if the non-compat version exists.
- if ($compat32 eq 'TRUE') {
- my $has64 = 'FALSE';
- my @inst = get_installed_sbos ();
- THIRD: for my $key (keys @inst) {
- $has64 = 'TRUE', last THIRD if $sbo eq $inst[$key]{name};
- }
- unless ($has64 eq 'TRUE') {
- print "\nYou are attempting to install $sbo-compat32, however,";
- print " $sbo is not yet installed. Shall I install it first?";
- print " [y] ";
- if (<STDIN> =~ /^[Yy\n]/) {
- my @args = ('/usr/sbin/sboupgrade', '-oN', $sbo);
- system (@args) == 0 or exit 1;
- } else {
- print "Please install $sbo\n" and exit 0;
- }
+INSTALL_NEW:
+exit 0 unless $install_new;
+my $todo_install;
+FIRST: for my $sbo (@ARGV) {
+ my $name = $compat32 ? "$sbo-compat32" : $sbo;
+ my $inst = get_installed_sbos;
+ my $inst_names = get_inst_names $inst;;
+ warn "$name already installed\n", next FIRST if $name ~~ @$inst_names;
+ # if compat32 is TRUE, we need to see if the non-compat version exists.
+ if ($compat32) {
+ my $inst = get_installed_sbos;
+ my $inst_names = get_inst_names $inst;
+ unless ($sbo ~~ @$inst_names) {
+ print "\nYou are attempting to install $name, however, $sbo is not";
+ print " yet installed. Shall I install it first? [y] ";
+ if (<STDIN> =~ /^[Yy\n]/) {
+ my @args = ('/usr/sbin/sboupgrade', '-oN', $sbo);
+ system (@args) == 0 or exit 1;
+ } else {
+ warn "Please install $sbo\n" and exit 0;
}
}
- $has eq 'TRUE' ? warn "$name already installed.\n" :
- push @todo_install, $sbo;
}
- @failed = process_sbos (@todo_install) if exists $todo_install[0];
- print_failures ();
+ $$todo_install{$sbo} = $locations{$sbo};
}
+@failures = process_sbos $todo_install if keys %$todo_install > 0;
+print_failures @failures;
-exit 1 if exists $failed[0];
exit 0;
diff --git a/t/SBO/Lib.pm b/t/SBO/Lib.pm
new file mode 100644
index 0000000..398e6a3
--- /dev/null
+++ b/t/SBO/Lib.pm
@@ -0,0 +1,668 @@
+#!/usr/bin/env perl
+#
+# vim: set ts=4:noet
+#
+# Lib.pm
+# shared functions for the sbo_ scripts.
+#
+# author: Jacob Pipkin <j@dawnrazor.net>
+# date: Setting Orange, the 37th day of Discord in the YOLD 3178
+# license: WTFPL <http://sam.zoy.org/wtfpl/COPYING>
+
+use 5.16.0;
+use warnings FATAL => 'all';
+use strict;
+
+package SBO::Lib 1.0;
+my $version = "1.0";
+
+require Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT = qw(get_slack_version chk_slackbuilds_txt check_home rsync_sbo_tree get_sbo_from_loc get_sbo_version get_download_info get_arch get_sbo_downloads get_filename_from_link compute_md5sum compare_md5s verify_distfile get_distfile get_symlink_from_filename check_x32 check_multilib rewrite_slackbuild revert_slackbuild check_distfiles create_symlinks grok_temp_file get_src_dir get_pkg_name perform_sbo do_convertpkg
+ script_error
+ open_fh
+ open_read
+ show_version
+ slackbuilds_or_fetch
+ fetch_tree
+ update_tree
+ get_installed_sbos
+ get_available_updates
+ do_slackbuild
+ make_clean
+ make_distclean
+ do_upgradepkg
+ get_sbo_location
+ get_from_info
+ get_tmp_fn
+);
+
+#$< == 0 or die "This script requires root privileges.\n";
+
+use Tie::File;
+use Sort::Versions;
+use Digest::MD5;
+use File::Copy;
+use File::Path qw(make_path remove_tree);
+use Fcntl;
+use File::Find;
+use File::Temp qw(tempdir tempfile);
+use Data::Dumper;
+use Fcntl qw(F_SETFD F_GETFD);
+
+our $tempdir = tempdir (CLEANUP => 1);
+
+# subroutine for throwing internal script errors
+sub script_error (;$) {
+ exists $_[0] ? die "A fatal script error has occured:\n$_[0]\nExiting.\n"
+ : die "A fatal script error has occured. Exiting.\n";
+}
+
+# sub for opening files, second arg is like '<','>', etc
+sub open_fh ($$) {
+ exists $_[1] or script_error 'open_fh requires two arguments';
+ -f $_[0] or script_error 'open_fh first argument not a file';
+ my ($file, $op) = @_;
+ open my $fh, $op, $file or die "Unable to open $file.\n";
+ return $fh;
+}
+
+sub open_read ($) {
+ return open_fh shift, '<';
+}
+
+# pull in configuration, set sane defaults, etc.
+our $conf_dir = '/etc/sbotools';
+our $conf_file = "$conf_dir/sbotools.conf";
+our %config = (
+ NOCLEAN => 'FALSE',
+ DISTCLEAN => 'FALSE',
+ JOBS => 'FALSE',
+ PKG_DIR => 'FALSE',
+ SBO_HOME => 'FALSE',
+);
+
+# if the conf file exists, pull all the $key=$value pairs into a hash
+my %conf_values;
+if (-f $conf_file) {
+ my $fh = open_read $conf_file;
+ my $text = do {local $/; <$fh>};
+ %conf_values = $text =~ /^(\w+)=(.*)$/mg;
+ close $fh;
+}
+
+for my $key (keys %config) {
+ $config{$key} = $conf_values{$key} if exists $conf_values{$key};
+}
+$config{JOBS} = 'FALSE' unless $config{JOBS} =~ /^\d+$/;
+$config{SBO_HOME} = '/usr/sbo' if $config{SBO_HOME} eq 'FALSE';
+
+# some stuff we'll need later.
+my $distfiles = "$config{SBO_HOME}/distfiles";
+my $slackbuilds_txt = "$config{SBO_HOME}/SLACKBUILDS.TXT";
+my $name_regex = '\ASLACKBUILD\s+NAME:\s+';
+
+sub show_version () {
+ say "sbotools version $version";
+ say 'licensed under the WTFPL';
+ say '<http://sam.zoy.org/wtfpl/COPYING>';
+}
+
+# %supported maps what's in /etc/slackware-version to what's at SBo
+# which is now not needed since this version drops support < 14.0
+# but it's already future-proofed, so leave it.
+sub get_slack_version () {
+ my %supported = ('14.0' => '14.0');
+ my $fh = open_read '/etc/slackware-version';
+ chomp (my $line = <$fh>);
+ close $fh;
+ my $version = ($line =~ /\s+(\d+[^\s]+)$/)[0];
+ die "Unsupported Slackware version: $version\n"
+ unless $version ~~ %supported;
+ return $supported{$version};
+}
+
+# does the SLACKBUILDS.TXT file exist in the sbo tree?
+sub chk_slackbuilds_txt () {
+ return -f $slackbuilds_txt ? 1 : 0;
+}
+
+# check for the validity of new $config{SBO_HOME}
+sub check_home () {
+ my $sbo_home = $config{SBO_HOME};
+ if (-d $sbo_home) {
+ opendir (my $home_handle, $sbo_home);
+ FIRST: while (readdir $home_handle) {
+ next FIRST if /^\.[\.]{0,1}$/;
+ die "$sbo_home exists and is not empty. Exiting.\n";
+ }
+ } else {
+ make_path ($sbo_home) or die "Unable to create $sbo_home.\n";
+ }
+}
+
+# rsync the sbo tree from slackbuilds.org to $config{SBO_HOME}
+sub rsync_sbo_tree () {
+ my $slk_version = get_slack_version;
+ my @arg = ('rsync', '-a', '--exclude=*.tar.gz', '--exclude=*.tar.gz.asc');
+ push @arg, "rsync://slackbuilds.org/slackbuilds/$slk_version/*";
+ my $out = system @arg, $config{SBO_HOME};
+ say 'Finished.' and return $out;
+}
+
+# wrappers for differing checks and output
+sub fetch_tree () {
+ check_home;
+ say 'Pulling SlackBuilds tree...';
+ rsync_sbo_tree, return $?;
+}
+
+sub update_tree () {
+ fetch_tree, return unless chk_slackbuilds_txt;
+ say 'Updating SlackBuilds tree...';
+ rsync_sbo_tree, return $?;
+}
+
+# if the SLACKBUILDS.TXT is not in $config{SBO_HOME}, we assume the tree has
+# not been populated there; prompt the user to automagickally pull the tree.
+sub slackbuilds_or_fetch () {
+ unless (chk_slackbuilds_txt) {
+ say 'It looks like you haven\'t run "sbosnap fetch" yet.';
+ print 'Would you like me to do this now? [y] ';
+ <STDIN> =~ /^[Yy\n]/ ? fetch_tree :
+ die "Please run \"sbosnap fetch\"\n";
+ }
+ return 1;
+}
+
+# pull an array of hashes, each hash containing the name and version of an sbo
+# currently installed.
+sub get_installed_sbos () {
+ my @installed;
+ # $1 == name, $2 == version
+ my $regex = qr#/([^/]+)-([^-]+)-[^-]+-[^-]+$#;
+ for my $path (</var/log/packages/*_SBo>) {
+ my ($name, $version) = ($path =~ $regex)[0,1];
+ push @installed, {name => $name, version => $version};
+ }
+ return \@installed;
+}
+
+# search the SLACKBUILDS.TXT for a given sbo's directory
+sub get_sbo_location ($) {
+ exists $_[0] or script_error 'get_sbo_location requires an argument.';
+ my $sbo = shift;
+ my $regex = qr#LOCATION:\s+\.(/[^/]+/$sbo)$#;
+ my $fh = open_read $slackbuilds_txt;
+ while (my $line = <$fh>) {
+ if (my $loc = ($line =~ $regex)[0]) {
+ return "$config{SBO_HOME}$loc";
+ }
+ }
+ return;
+}
+
+# pull the sbo name from a $location: $config{SBO_HOME}/system/wine, etc.
+sub get_sbo_from_loc ($) {
+ exists $_[0] or script_error 'get_sbo_from_loc requires an argument.';
+ return (shift =~ qr#/([^/]+)$#)[0];
+}
+
+# pull piece(s) of data, GET, from the $sbo.info file under LOCATION.
+sub get_from_info (%) {
+ my %args = (
+ LOCATION => '',
+ GET => '',
+ @_
+ );
+ unless ($args{LOCATION} && $args{GET}) {
+ script_error 'get_from_info requires LOCATION and GET.';
+ }
+ state $vars = {PRGNAM => ['']};
+ my $sbo = get_sbo_from_loc $args{LOCATION};
+ return $$vars{$args{GET}} if $$vars{PRGNAM}[0] eq $sbo;
+ # if we're here, we haven't read in the .info file yet.
+ my $fh = open_read "$args{LOCATION}/$sbo.info";
+ # suck it all in, clean it all up, stuff it all in $vars.
+ my $contents = do {local $/; <$fh>};
+ $contents =~ s/("|\\\n)//g;
+ $vars = {$contents =~ /^(\w+)=(.*)$/mg};
+ # fill the hash with array refs - even for single values,
+ # since consistency here is a lot easier than sorting it out later
+ for my $key (keys %$vars) {
+ if ($$vars{$key} =~ /\s/) {
+ my @array = split ' ', $$vars{$key};
+ $$vars{$key} = \@array;
+ } else {
+ $$vars{$key} = [$$vars{$key}];
+ }
+ }
+ return exists $$vars{$args{GET}} ? $$vars{$args{GET}} : 0;
+}
+
+# find the version in the tree for a given sbo (provided a location)
+sub get_sbo_version ($) {
+ exists $_[0] or script_error 'get_sbo_version requires an argument.';
+ my $version = get_from_info (LOCATION => shift, GET => 'VERSION');
+ return $$version[0] ? $$version[0] : 0;
+}
+
+# for each installed sbo, find out whether or not the version in the tree is
+# newer, and compile an array of hashes containing those which are
+sub get_available_updates () {
+ my @updates;
+ my $pkg_list = get_installed_sbos;
+ FIRST: for my $key (keys @$pkg_list) {
+ my $location = get_sbo_location $$pkg_list[$key]{name};
+ # if we can't find a location, assume invalid and skip
+ next FIRST unless defined $location;
+ my $version = get_sbo_version $location;
+ if (versioncmp ($version, $$pkg_list[$key]{version}) == 1) {
+ push @updates, {
+ name => $$pkg_list[$key]{name},
+ installed => $$pkg_list[$key]{version},
+ update => $version
+ };
+ }
+ }
+ return \@updates;
+}
+
+# get downloads and md5sums from an sbo's .info file, first
+# checking for x86_64-specific info if we are told to
+sub get_download_info (%) {
+ my %args = (
+ LOCATION => 0,
+ X64 => 1,
+ @_
+ );
+ $args{LOCATION} or script_error 'get_download_info requires LOCATION.';
+ my ($get, $downs, $md5s, %return);
+ $get = ($args{X64} ? 'DOWNLOAD_x86_64' : 'DOWNLOAD');
+ $downs = get_from_info (LOCATION => $args{LOCATION}, GET => $get);
+ # did we get nothing back, or UNSUPPORTED/UNTESTED?
+ if ($args{X64}) {
+ my $nothing;
+ if (! $$downs[0]) {
+ $nothing = 1;
+ } elsif ($$downs[0] =~ qr/^UN(SUPPOR|TES)TED$/) {
+ $nothing = 1;
+ }
+ if ($nothing) {
+ $args{X64} = 0;
+ $downs = get_from_info (LOCATION => $args{LOCATION},
+ GET => 'DOWNLOAD');
+ }
+ }
+ # if we still don't have any links, something is really wrong.
+ return unless $$downs[0];
+ # grab the md5s and build a hash
+ $get = $args{X64} ? 'MD5SUM_x86_64' : 'MD5SUM';
+ $md5s = get_from_info (LOCATION => $args{LOCATION}, GET => $get);
+ return unless $$md5s[0];
+ $return{$$downs[$_]} = $$md5s[$_] for (keys @$downs);
+ return %return;
+}
+
+sub get_arch () {
+ chomp (my $arch = `uname -m`);
+ return $arch;
+}
+
+# TODO: should probably combine this with get_download_info
+sub get_sbo_downloads (%) {
+ my %args = (
+ LOCATION => '',
+ 32 => 0,
+ @_
+ );
+ $args{LOCATION} or script_error 'get_sbo_downloads requires LOCATION.';
+ my $location = $args{LOCATION};
+ -d $location or script_error 'get_sbo_downloads given a non-directory.';
+ my $arch = get_arch;
+ my %dl_info;
+ if ($arch eq 'x86_64') {
+ %dl_info = get_download_info (LOCATION => $location) unless $args{32};
+ }
+ unless (keys %dl_info > 0) {
+ %dl_info = get_download_info (LOCATION => $location, X64 => 0);
+ }
+ return %dl_info;
+}
+
+# given a link, grab the filename from the end of it
+sub get_filename_from_link ($) {
+ exists $_[0] or script_error 'get_filename_from_link requires an argument';
+ return "$distfiles/". (shift =~ qr#/([^/]+)$#)[0];
+}
+
+# for a given file, computer its md5sum
+sub compute_md5sum ($) {
+ -f $_[0] or script_error 'compute_md5sum requires a file argument.';
+ my $fh = open_read shift;
+ my $md5 = Digest::MD5->new;
+ $md5->addfile ($fh);
+ my $md5sum = $md5->hexdigest;
+ close $fh;
+ return $md5sum;
+}
+
+sub compare_md5s ($$) {
+ exists $_[1] or script_error 'compare_md5s requires two arguments.';
+ my ($first, $second) = @_;
+ return $first eq $second ? 1 : 0;
+}
+
+# for a given distfile, see whether or not it exists, and if so, if its md5sum
+# matches the sbo's .info file
+sub verify_distfile ($$) {
+ exists $_[1] or script_error 'check_distfile requires two arguments.';
+ my ($link, $info_md5sum) = @_;
+ my $filename = get_filename_from_link $link;
+ return unless -d $distfiles;
+ return unless -f $filename;
+ my $md5sum = compute_md5sum $filename;
+ return compare_md5s $info_md5sum, $md5sum;
+}
+
+# for a given distfile, attempt to retrieve it and, if successful, check its
+# md5sum against that in the sbo's .info file
+sub get_distfile ($$) {
+ exists $_[1] or script_error 'get_distfile requires an argument';
+ my ($link, $exp_md5) = @_;
+ my $filename = get_filename_from_link $link;
+ mkdir $distfiles unless -d $distfiles;
+ chdir $distfiles;
+ system ("wget --no-check-certificate $link") == 0 or
+ die "Unable to wget $link\n";
+ my $md5sum = compute_md5sum $filename;
+ # can't do anything if the link in the .info doesn't lead to a good d/l
+ compare_md5s $md5sum, $exp_md5 or die "md5sum failure for $filename.\n";
+ return 1;
+}
+
+# for a given distfile, figure out what the full path to its symlink will be
+sub get_symlink_from_filename ($$) {
+ exists $_[1] or script_error
+ 'get_symlink_from_filename requires two arguments';
+ -f $_[0] or script_error
+ 'get_symlink_from_filename first argument is not a file';
+ my ($filename, $location) = @_;
+ return "$location/". ($filename =~ qr#/([^/]+)$#)[0];
+}
+
+# determine whether or not a given sbo is 32-bit only
+sub check_x32 ($) {
+ exists $_[0] or script_error 'check_x32 requires an argument.';
+ my $dl = get_from_info (LOCATION => shift, GET => 'DOWNLOAD_x86_64');
+ return $$dl[0] =~ /UN(SUPPOR|TES)TED/ ? 1 : 0;
+}
+
+# can't do 32-bit on x86_64 without this file, so we'll use it as the test to
+# to determine whether or not an x86_64 system is setup for multilib
+sub check_multilib () {
+ return 1 if -f '/etc/profile.d/32dev.sh';
+ return;
+}
+
+# make a backup of the existent SlackBuild, and rewrite the original as needed
+sub rewrite_slackbuild ($$%) {
+ exists $_[1] or script_error 'rewrite_slackbuild requires two arguments.';
+ my ($slackbuild, $tempfn, %changes) = @_;
+ copy ($slackbuild, "$slackbuild.orig") or
+ die "Unable to backup $slackbuild to $slackbuild.orig\n";
+ my $tar_regex = qr/(un|)tar .*$/;
+ my $makepkg_regex = qr/makepkg/;
+ my $libdir_regex = qr/^\s*LIBDIRSUFFIX="64"\s*$/;
+ my $make_regex = qr/^\s*make(| \Q||\E exit 1)$/;
+ my $arch_regex = qr/\$VERSION-\$ARCH-\$BUILD/;
+ # tie the slackbuild, because this is the easiest way to handle this.
+ tie my @sb_file, 'Tie::File', $slackbuild;
+ for my $line (@sb_file) {
+ # get the output of the tar and makepkg commands. hope like hell that v
+ # is specified among tar's arguments
+ if ($line =~ $tar_regex || $line =~ $makepkg_regex) {
+ $line = "$line | tee -a $tempfn";
+ }
+ # then check for and apply any other %changes
+ if (exists $changes{libdirsuffix}) {
+ $line =~ s/64/$changes{libdirsuffix}/ if $line =~ $libdir_regex;
+ }
+ if (exists $changes{make}) {
+ $line =~ s/make/make $changes{make}/ if $line =~ $make_regex;
+ }
+ if (exists $changes{arch_out}) {
+ $line =~ s/\$ARCH/$changes{arch_out}/ if $line =~ $arch_regex;
+ }
+ }
+ untie @sb_file;
+ return 1;
+}
+
+# move a backed-up .SlackBuild file back into place
+sub revert_slackbuild ($) {
+ exists $_[0] or script_error 'revert_slackbuild requires an argument';
+ my $slackbuild = shift;
+ if (-f "$slackbuild.orig") {
+ unlink $slackbuild if -f $slackbuild;
+ rename ("$slackbuild.orig", $slackbuild);
+ }
+ return 1;
+}
+
+# for each $download, see if we have it, and if the copy we have is good,
+# otherwise download a new copy
+sub check_distfiles (%) {
+ exists $_[0] or script_error 'check_distfiles requires an argument.';
+ my %dists = @_;
+ for my $link (keys %dists) {
+ my $md5sum = $dists{$link};
+ unless (verify_distfile $link, $md5sum) {
+ die unless get_distfile $link, $md5sum;
+ }
+ }
+ return 1;
+}
+
+# given a location and a list of download links, assemble a list of symlinks,
+# and create them.
+sub create_symlinks ($%) {
+ exists $_[1] or script_error 'create_symlinks requires two arguments.';
+ my ($location, %downloads) = @_;
+ my @symlinks;
+ for my $link (keys %downloads) {
+ my $filename = get_filename_from_link $link;
+ my $symlink = get_symlink_from_filename $filename, $location;
+ push @symlinks, $symlink;
+ symlink $filename, $symlink;
+ }
+ return @symlinks;
+}
+
+# pull the untarred source directory or created package name from the temp
+# file (the one we tee'd to)
+sub grok_temp_file (%) {
+ my %args = (
+ FH => '',
+ REGEX => '',
+ CAPTURE => 0,
+ @_
+ );
+ unless ($args{FH} && $args{REGEX}) {
+ script_error 'grok_temp_file requires two arguments';
+ }
+ my $fh = $args{FH};
+ seek $fh, 0, 0;
+ my $out;
+ FIRST: while (my $line = <$fh>) {
+ if ($line =~ $args{REGEX}) {
+ $out = ($line =~ $args{REGEX})[$args{CAPTURE}];
+ last FIRST;
+ }
+ }
+# close $fh;
+ return $out;
+}
+
+# wrappers around grok_temp_file
+sub get_src_dir ($) {
+ exists $_[0] or script_error 'get_src_dir requires an argument';
+ return grok_temp_file (FH => shift, REGEX => qr#^([^/]+)/#);
+}
+
+sub get_pkg_name ($) {
+ exists $_[0] or script_error 'get_pkg_name requires an argument';
+ return grok_temp_file (FH => shift,
+ REGEX => qr/^Slackware\s+package\s+([^\s]+)\s+created\.$/);
+}
+
+sub get_tmp_fn ($) {
+ exists $_[0] or script_error 'get_tmp_fn requires an argument.';
+ my $fh = shift;
+ fcntl ($fh, F_SETFD, 0) or die "no unset exec-close thingy\n";
+ return "/dev/fd/". fileno $fh;
+}
+
+# prep and run .SlackBuild
+sub perform_sbo (%) {
+ my %args = (
+ OPTS => 0,
+ JOBS => 0,
+ LOCATION => '',
+ ARCH => '',
+ C32 => 0,
+ X32 => 0,
+ @_
+ );
+ unless ($args{LOCATION} && $args{ARCH}) {
+ script_error 'perform_sbo requires LOCATION and ARCH.';
+ }
+ my $location = $args{LOCATION};
+ my $sbo = get_sbo_from_loc $location;
+ my ($cmd, %changes);
+ # figure out any changes we need to make to the .SlackBuild
+ $changes{make} = "-j $args{JOBS}" if $args{JOBS};
+ if ($args{ARCH} eq 'x86_64' and ($args{C32} || $args{X32})) {
+ if ($args{C32}) {
+ $changes{libdirsuffix} = '';
+ } elsif ($args{X32}) {
+ $changes{arch_out} = 'i486';
+ }
+ $cmd = ". /etc/profile.d/32dev.sh &&";
+ }
+ $cmd .= "/bin/sh $location/$sbo.SlackBuild";
+ $cmd = "$args{OPTS} $cmd" if $args{OPTS};
+ my $tempfh = tempfile (DIR => $tempdir);
+ my $fn = get_tmp_fn $tempfh;
+ rewrite_slackbuild "$location/$sbo.SlackBuild", $fn, %changes;
+ chdir $location, my $out = system $cmd;
+ revert_slackbuild "$location/$sbo.SlackBuild";
+ die unless $out == 0;
+ my $pkg = get_pkg_name $tempfh;
+ my $src = get_src_dir $tempfh;
+ return $pkg, $src;
+}
+
+# run convertpkg on a package to turn it into a -compat32 thing
+sub do_convertpkg ($) {
+ exists $_[0] or script_error 'do_convertpkg requires an argument.';
+ my $pkg = shift;
+ my $tempfh = tempfile (DIR => $tempdir);
+ my $fn = get_tmp_fn $tempfh;
+ my $cmd = "/usr/sbin/convertpkg-compat32 -i $pkg -d /tmp | tee $fn";
+ system ($cmd) == 0 or die;
+ unlink $pkg;
+ return get_pkg_name $tempfh;
+}
+
+# "public interface", sort of thing.
+sub do_slackbuild (%) {
+ my %args = (
+ OPTS => 0,
+ JOBS => 0,
+ LOCATION => '',
+ COMPAT32 => 0,
+ @_
+ );
+ $args{LOCATION} or script_error 'do_slackbuild requires LOCATION.';
+ my $location = $args{LOCATION};
+ my $sbo = get_sbo_from_loc $location;
+ my $arch = get_arch;
+ my $multi = check_multilib;
+ my $version = get_sbo_version $location;
+ my $x32;
+ # ensure x32 stuff is set correctly, or that we're setup for it
+ if ($args{COMPAT32}) {
+ die "compat32 only works on x86_64.\n" unless $arch eq 'x86_64';
+ die "compat32 requires multilib.\n" unless $multi;
+ die "compat32 requires /usr/sbin/convertpkg-compat32.\n"
+ unless -f '/usr/sbin/convertpkg-compat32';
+ } else {
+ if ($arch eq 'x86_64') {
+ $x32 = check_x32 $args{LOCATION};
+ if ($x32 && ! $multi) {
+ die "$sbo is 32-bit which requires multilib on x86_64.\n";
+ }
+ }
+ }
+ # get a hash of downloads and md5sums, ensure we have 'em, symlink 'em
+ my %downloads = get_sbo_downloads (
+ LOCATION => $location,
+ 32 => $args{COMPAT32}
+ );
+ check_distfiles %downloads;
+ my @symlinks = create_symlinks $args{LOCATION}, %downloads;
+ # setup and run the .SlackBuild itself
+ my ($pkg, $src) = perform_sbo (
+ OPTS => $args{OPTS},
+ JOBS => $args{JOBS},
+ LOCATION => $location,
+ ARCH => $arch,
+ C32 => $args{COMPAT32},
+ X32 => $x32,
+ );
+ do_convertpkg $pkg if $args{COMPAT32};
+ unlink $_ for @symlinks;
+ return $version, $pkg, $src;
+}
+
+# remove work directories (source and packaging dirs under /tmp/SBo)
+sub make_clean ($$$) {
+ exists $_[2] or script_error 'make_clean requires three arguments.';
+ my ($sbo, $src, $version) = @_;
+ say "Cleaning for $sbo-$version...";
+ my $tmpsbo = "/tmp/SBo";
+ remove_tree ("$tmpsbo/$src") if -d "$tmpsbo/$src";
+ remove_tree ("$tmpsbo/package-$sbo") if -d "$tmpsbo/package-$sbo";
+ return 1;
+}
+
+# remove distfiles
+sub make_distclean (%) {
+ my %args = (
+ SRC => '',
+ VERSION => '',
+ LOCATION => '',
+ @_
+ );
+ unless ($args{SRC} && $args{VERSION} && $args{LOCATION}) {
+ script_error 'make_distclean requires four arguments.';
+ }
+ my $sbo = get_sbo_from_loc $args{LOCATION};
+ make_clean $sbo, $args{SRC}, $args{VERSION};
+ say "Distcleaning for $sbo-$args{VERSION}...";
+ # remove any distfiles for this particular SBo.
+ my %downloads = get_sbo_downloads (LOCATION => $args{LOCATION});
+ for my $key (keys %downloads) {
+ my $filename = get_filename_from_link $key;
+ unlink $filename if -f $filename;
+ }
+ return 1;
+}
+
+# run upgradepkg for a created package
+sub do_upgradepkg ($) {
+ exists $_[0] or script_error 'do_upgradepkg requires an argument.';
+ system ('/sbin/upgradepkg', '--reinstall', '--install-new', shift);
+ return 1;
+}
diff --git a/t/SBO/Lib.pm~ b/t/SBO/Lib.pm~
new file mode 100644
index 0000000..be29986
--- /dev/null
+++ b/t/SBO/Lib.pm~
@@ -0,0 +1,643 @@
+#!/usr/bin/env perl
+#
+# vim: set ts=4:noet
+#
+# Lib.pm
+# shared functions for the sbo_ scripts.
+#
+# author: Jacob Pipkin <j@dawnrazor.net>
+# date: Setting Orange, the 37th day of Discord in the YOLD 3178
+# license: WTFPL <http://sam.zoy.org/wtfpl/COPYING>
+
+use 5.16.0;
+use warnings FATAL => 'all';
+use strict;
+
+package SBO::Lib 1.0;
+my $version = "1.0";
+
+require Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT = qw(get_slack_version chk_slackbuilds_txt check_home rsync_sbo_tree get_sbo_from_loc get_sbo_version get_download_info get_arch get_sbo_downloads get_filename_from_link compute_md5sum compare_md5s check_distfile get_distfile get_symlink_from_filename check_x32 check_multilib rewrite_slackbuild revert_slackbuild create_symlinks grok_temp_file get_src_dir get_pkg_name perform_sbo
+ script_error
+ open_fh
+ open_read
+ show_version
+ slackbuilds_or_fetch
+ fetch_tree
+ update_tree
+ get_installed_sbos
+ get_available_updates
+ do_slackbuild
+ make_clean
+ make_distclean
+ do_upgradepkg
+ get_sbo_location
+ get_from_info
+);
+
+#$< == 0 or die "This script requires root privileges.\n";
+
+use Tie::File;
+use Sort::Versions;
+use Digest::MD5;
+use File::Copy;
+use File::Path qw(make_path remove_tree);
+use Fcntl;
+use File::Find;
+use File::Temp qw(tempdir tempfile);
+use Data::Dumper;
+
+our $tempdir = tempdir (CLEANUP => 1);
+
+# subroutine for throwing internal script errors
+sub script_error (;$) {
+ exists $_[0] ? die "A fatal script error has occured:\n$_[0]\nExiting.\n"
+ : die "A fatal script error has occured. Exiting.\n";
+}
+
+# sub for opening files, second arg is like '<','>', etc
+sub open_fh ($$) {
+ exists $_[1] or script_error 'open_fh requires two arguments';
+ -f $_[0] or script_error 'open_fh first argument not a file';
+ my ($file, $op) = @_;
+ open my $fh, $op, $file or die "Unable to open $file.\n";
+ return $fh;
+}
+
+sub open_read ($) {
+ return open_fh shift, '<';
+}
+
+# pull in configuration, set sane defaults, etc.
+our $conf_dir = '/etc/sbotools';
+our $conf_file = "$conf_dir/sbotools.conf";
+our %config = (
+ NOCLEAN => 'FALSE',
+ DISTCLEAN => 'FALSE',
+ JOBS => 'FALSE',
+ PKG_DIR => 'FALSE',
+ SBO_HOME => 'FALSE',
+);
+
+# if the conf file exists, pull all the $key=$value pairs into a hash
+my %conf_values;
+if (-f $conf_file) {
+ my $fh = open_read $conf_file;
+ my $text = do {local $/; <$fh>};
+ %conf_values = $text =~ /^(\w+)=(.*)$/mg;
+ close $fh;
+}
+
+for my $key (keys %config) {
+ $config{$key} = $conf_values{$key} if exists $conf_values{$key};
+}
+$config{JOBS} = 'FALSE' unless $config{JOBS} =~ /^\d+$/;
+$config{SBO_HOME} = '/usr/sbo' if $config{SBO_HOME} eq 'FALSE';
+
+# some stuff we'll need later.
+my $distfiles = "$config{SBO_HOME}/distfiles";
+my $slackbuilds_txt = "$config{SBO_HOME}/SLACKBUILDS.TXT";
+my $name_regex = '\ASLACKBUILD\s+NAME:\s+';
+
+sub show_version () {
+ print "sbotools version $version\n";
+ print "licensed under the WTFPL\n";
+ print "<http://sam.zoy.org/wtfpl/COPYING>\n";
+}
+
+# %supported maps what's in /etc/slackware-version to what's at SBo
+# which is now not needed since this version drops support < 14.0
+# but it's already future-proofed, so leave it.
+sub get_slack_version () {
+ my %supported = ('14.0' => '14.0');
+ my $fh = open_read '/etc/slackware-version';
+ chomp (my $line = <$fh>);
+ close $fh;
+ my $version = ($line =~ /\s+(\d+[^\s]+)$/)[0];
+ die "Unsupported Slackware version: $version\n"
+ unless $version ~~ %supported;
+ return $supported{$version};
+}
+
+# does the SLACKBUILDS.TXT file exist in the sbo tree?
+sub chk_slackbuilds_txt () {
+ return -f $slackbuilds_txt ? 1 : 0;
+}
+
+# check for the validity of new $config{SBO_HOME}
+sub check_home () {
+ my $sbo_home = $config{SBO_HOME};
+ if (-d $sbo_home) {
+ opendir (my $home_handle, $sbo_home);
+ FIRST: while (readdir $home_handle) {
+ next FIRST if /^\.[\.]{0,1}$/;
+ die "$sbo_home exists and is not empty. Exiting.\n";
+ }
+ } else {
+ make_path ($sbo_home) or die "Unable to create $sbo_home.\n";
+ }
+}
+
+# rsync the sbo tree from slackbuilds.org to $config{SBO_HOME}
+sub rsync_sbo_tree () {
+ my $slk_version = get_slack_version;
+ my @arg = ('rsync', '-a', '--exclude=*.tar.gz', '--exclude=*.tar.gz.asc');
+ push @arg, "rsync://slackbuilds.org/slackbuilds/$slk_version/*";
+ my $out = system @arg, $config{SBO_HOME};
+ print "Finished.\n" and return $out;
+}
+
+# wrappers for differing checks and output
+sub fetch_tree () {
+ check_home;
+ print "Pulling SlackBuilds tree...\n";
+ rsync_sbo_tree, return $?;
+}
+
+sub update_tree () {
+ fetch_tree, return unless chk_slackbuilds_txt;
+ print "Updating SlackBuilds tree...\n";
+ rsync_sbo_tree, return $?;
+}
+
+# if the SLACKBUILDS.TXT is not in $config{SBO_HOME}, we assume the tree has
+# not been populated there; prompt the user to automagickally pull the tree.
+sub slackbuilds_or_fetch () {
+ unless (chk_slackbuilds_txt) {
+ print "It looks like you haven't run \"sbosnap fetch\" yet.\n";
+ print "Would you like me to do this now? [y] ";
+ <STDIN> =~ /^[Yy\n]/ ? fetch_tree :
+ die "Please run \"sbosnap fetch\"\n";
+ }
+ return 1;
+}
+
+# pull an array of hashes, each hash containing the name and version of an sbo
+# currently installed.
+sub get_installed_sbos () {
+ my @installed;
+ # $1 == name, $2 == version
+ my $regex = qr#/([^/]+)-([^-]+)-[^-]+-[^-]+$#;
+ for my $path (</var/log/packages/*_SBo>) {
+ my ($name, $version) = ($path =~ $regex)[0,1];
+ push @installed, {name => $name, version => $version};
+ }
+ return \@installed;
+}
+
+# search the SLACKBUILDS.TXT for a given sbo's directory
+sub get_sbo_location ($) {
+ exists $_[0] or script_error 'get_sbo_location requires an argument.';
+ my $sbo = shift;
+ my $regex = qr#LOCATION:\s+\.(/[^/]+/$sbo)$#;
+ my $fh = open_read $slackbuilds_txt;
+ while (my $line = <$fh>) {
+ if (my $loc = ($line =~ $regex)[0]) {
+ return "$config{SBO_HOME}$loc";
+ }
+ }
+ return;
+}
+
+# pull the sbo name from a $location: $config{SBO_HOME}/system/wine, etc.
+sub get_sbo_from_loc ($) {
+ exists $_[0] or script_error 'get_sbo_from_loc requires an argument.';
+ return (shift =~ qr#/([^/]+)$#)[0];
+}
+
+# pull piece(s) of data, GET, from the $sbo.info file under LOCATION.
+sub get_from_info {
+ warn " * * * * * * * * * * in get_from_info sub * * * * * * * * * *\n";
+ my %args = (
+ LOCATION => '',
+ GET => '',
+ @_
+ );
+ unless ($args{LOCATION} && $args{GET}) {
+ script_error 'get_from_info requires LOCATION and GET.';
+ }
+ state $vars = {PKGNAM => ['']};
+ my $sbo = get_sbo_from_loc $args{LOCATION};
+ print Dumper ($vars);
+ return $$vars{$args{GET}} if $$vars{PKGNAM}[0] eq $sbo;
+ # if we haven't read in the .info file yet, do so now.
+ warn " * * * * * * * * * * parsing $sbo.info file * * * * * * * * * *\n";
+ my $fh = open_read "$args{LOCATION}/$sbo.info";
+ # suck it all in and join up the \ lines...
+ my $contents = do {local $/; <$fh>};
+ $contents =~ s/("|\\\n)//g;
+ my %tmp = $contents =~ /^(\w+)=(.*)$/mg;
+ $vars = \%tmp;
+ # fill the hash with array refs - even for single values,
+ # since consistency here is a lot easier than sorting it out later
+ for my $key (keys %$vars) {
+ if ($$vars{$key} =~ /\s/) {
+ my @array = split ' ', $$vars{$key};
+ $$vars{$key} = \@array;
+ } else {
+ $$vars{$key} = [$$vars{$key}];
+ }
+ }
+ return $$vars{$args{GET}};
+}
+
+# find the version in the tree for a given sbo (provided a location)
+sub get_sbo_version ($) {
+ exists $_[0] or script_error 'get_sbo_version requires an argument.';
+ my $version = get_from_info (LOCATION => shift, GET => 'VERSION');
+ return $$version[0] ? $$version[0] : 0;
+}
+
+# for each installed sbo, find out whether or not the version in the tree is
+# newer, and compile an array of hashes containing those which are
+sub get_available_updates () {
+ my @updates;
+ my $pkg_list = get_installed_sbos;
+ FIRST: for my $key (keys @$pkg_list) {
+ my $location = get_sbo_location $$pkg_list[$key]{name};
+ # if we can't find a location, assume invalid and skip
+ next FIRST unless defined $location;
+ my $version = get_sbo_version $location;
+ if (versioncmp ($version, $$pkg_list[$key]{version}) == 1) {
+ push @updates, {
+ name => $$pkg_list[$key]{name},
+ installed => $$pkg_list[$key]{version},
+ update => $version
+ };
+ }
+ }
+ return \@updates;
+}
+
+# get downloads and md5sums from an sbo's .info file, first
+# checking for x86_64-specific info if we are told to
+sub get_download_info {
+ my %args = (
+ LOCATION => 0,
+ X64 => 1,
+ @_
+ );
+ $args{LOCATION} or script_error 'get_download_info requires LOCATION.';
+ my ($get, $downs, $md5s, %return);
+ $get = ($args{X64} ? 'DOWNLOAD_x86_64' : 'DOWNLOAD');
+ $downs = get_from_info (LOCATION => $args{LOCATION}, GET => $get);
+ # did we get nothing back, or UNSUPPORTED/UNTESTED?
+ if ($args{X64}) {
+ my $nothing;
+ if (! $$downs[0]) {
+ $nothing = 1;
+ } elsif ($$downs[0] =~ qr/^UN(SUPPOR|TES)TED$/) {
+ $nothing = 1;
+ }
+ if ($nothing) {
+ $args{X64} = 0;
+ $downs = get_from_info (LOCATION => $args{LOCATION},
+ GET => 'DOWNLOAD');
+ }
+ }
+ # if we still don't have any links, something is really wrong.
+ return unless $$downs[0];
+ # grab the md5s and build a hash
+ $get = $args{X64} ? 'MD5SUM_x86_64' : 'MD5SUM';
+ $md5s = get_from_info (LOCATION => $args{LOCATION}, GET => $get);
+ return unless $$md5s[0];
+ $return{$$downs[$_]} = $$md5s[$_] for (keys @$downs);
+ return %return;
+}
+
+sub get_arch () {
+ chomp (my $arch = `uname -m`);
+ return $arch;
+}
+
+# TODO: should probably combine this with get_download_info
+sub get_sbo_downloads {
+ my %args = (
+ LOCATION => '',
+ 32 => 0,
+ @_
+ );
+ $args{LOCATION} or script_error 'get_sbo_downloads requires LOCATION.';
+ my $location = $args{LOCATION};
+ -d $location or script_error 'get_sbo_downloads given a non-directory.';
+ my $arch = get_arch;
+ my %dl_info;
+ if ($arch eq 'x86_64') {
+ %dl_info = get_download_info (LOCATION => $location) unless $args{32};
+ }
+ unless (keys %dl_info > 0) {
+ %dl_info = get_download_info (LOCATION => $location, X64 => 0);
+ }
+ return %dl_info;
+}
+
+# given a link, grab the filename from the end of it
+sub get_filename_from_link ($) {
+ exists $_[0] or script_error 'get_filename_from_link requires an argument';
+ return "$distfiles/". (shift =~ qr#/([^/]+)$#)[0];
+}
+
+# for a given file, computer its md5sum
+sub compute_md5sum ($) {
+ -f $_[0] or script_error 'compute_md5sum requires a file argument.';
+ my $fh = open_read shift;
+ my $md5 = Digest::MD5->new;
+ $md5->addfile ($fh);
+ my $md5sum = $md5->hexdigest;
+ close $fh;
+ return $md5sum;
+}
+
+sub compare_md5s ($$) {
+ exists $_[1] or script_error 'compare_md5s requires two arguments.';
+ my ($first, $second) = @_;
+ return $first eq $second ? 1 : 0;
+}
+
+# for a given distfile, see whether or not it exists, and if so, if its md5sum
+# matches the sbo's .info file
+sub check_distfile ($$) {
+ exists $_[1] or script_error 'check_distfile requires two arguments.';
+ my ($link, $info_md5sum) = @_;
+ my $filename = get_filename_from_link $link;
+ return unless -d $distfiles;
+ return unless -f $filename;
+ my $md5sum = compute_md5sum $filename;
+ return compare_md5s $info_md5sum, $md5sum;
+}
+
+# for a given distfile, attempt to retrieve it and, if successful, check its
+# md5sum against that in the sbo's .info file
+sub get_distfile ($$) {
+ exists $_[1] or script_error 'get_distfile requires an argument';
+ my ($link, $exp_md5) = @_;
+ my $filename = get_filename_from_link $link;
+ mkdir $distfiles unless -d $distfiles;
+ chdir $distfiles;
+ system ("wget --no-check-certificate $link") == 0 or
+ die "Unable to wget $link\n";
+ my $md5sum = compute_md5sum $filename;
+ # can't do anything if the link in the .info doesn't lead to a good d/l
+ compare_md5s $md5sum, $exp_md5 or die "md5sum failure for $filename.\n";
+ return 1;
+}
+
+# for a given distfile, figure out what the full path to its symlink will be
+sub get_symlink_from_filename ($$) {
+ exists $_[1] or script_error
+ 'get_symlink_from_filename requires two arguments';
+ -f $_[0] or script_error
+ 'get_symlink_from_filename first argument is not a file';
+ my ($filename, $location) = @_;
+ return "$location/". ($filename =~ qr#/([^/]+)$#)[0];
+}
+
+# determine whether or not a given sbo is 32-bit only
+sub check_x32 ($) {
+ exists $_[0] or script_error 'check_x32 requires an argument.';
+ my $dl = get_from_info (LOCATION => shift, GET => 'DOWNLOAD_x86_64');
+ return $$dl[0] =~ /UN(SUPPOR|TES)TED/ ? 1 : 0;
+}
+
+# can't do 32-bit on x86_64 without this file, so we'll use it as the test to
+# to determine whether or not an x86_64 system is setup for multilib
+sub check_multilib () {
+ return 1 if -f '/etc/profile.d/32dev.sh';
+ return;
+}
+
+# make a backup of the existent SlackBuild, and rewrite the original as needed
+sub rewrite_slackbuild ($$%) {
+ exists $_[1] or script_error 'rewrite_slackbuild requires two arguments.';
+ my ($slackbuild, $tempfn, %changes) = @_;
+ copy ($slackbuild, "$slackbuild.orig") or
+ die "Unable to backup $slackbuild to $slackbuild.orig\n";
+ my $tar_regex = qr/(un|)tar .*$/;
+ my $makepkg_regex = qr/makepkg/;
+ my $libdir_regex = qr/^\s*LIBDIRSUFFIX="64"\s*$/;
+ my $make_regex = qr/^\s*make(| \Q||\E exit 1)$/;
+ my $arch_regex = qr/\$VERSION-\$ARCH-\$BUILD/;
+ # tie the slackbuild, because this is the easiest way to handle this.
+ tie my @sb_file, 'Tie::File', $slackbuild;
+ for my $line (@sb_file) {
+ # get the output of the tar and makepkg commands. hope like hell that v
+ # is specified among tar's arguments
+ if ($line =~ $tar_regex || $line =~ $makepkg_regex) {
+ $line = "$line | tee -a $tempfn";
+ }
+ # then check for and apply any other %changes
+ if (exists $changes{libdirsuffix}) {
+ $line =~ s/64/$changes{libdirsuffix}/ if $line =~ $libdir_regex;
+ }
+ if (exists $changes{make}) {
+ $line =~ s/make/make $changes{make}/ if $line =~ $make_regex;
+ }
+ if (exists $changes{arch_out}) {
+ $line =~ s/\$ARCH/$changes{arch_out}/ if $line =~ $arch_regex;
+ }
+ }
+ untie @sb_file;
+ return 1;
+}
+
+# move a backed-up .SlackBuild file back into place
+sub revert_slackbuild ($) {
+ exists $_[0] or script_error 'revert_slackbuild requires an argument';
+ my $slackbuild = shift;
+ if (-f "$slackbuild.orig") {
+ unlink $slackbuild if -f $slackbuild;
+ rename ("$slackbuild.orig", $slackbuild);
+ }
+ return 1;
+}
+
+# given a location and a list of download links, assemble a list of symlinks,
+# and create them.
+sub create_symlinks ($%) {
+ exists $_[1] or script_error 'create_symlinks requires two arguments.';
+ my ($location, %downloads) = @_;
+ my @symlinks;
+ for my $link (keys %downloads) {
+ my $md5sum = $downloads{$link};
+ unless (check_distfile $link, $md5sum) {
+ die unless get_distfile $link, $md5sum;
+ }
+ my $filename = get_filename_from_link $link;
+ my $symlink = get_symlink_from_filename $filename, $location;
+ push @symlinks, $symlink;
+ symlink $filename, $symlink;
+ }
+ return @symlinks;
+}
+
+# pull the untarred source directory or created package name from the temp
+# file (the one we tee'd to)
+sub grok_temp_file {
+ my %args = (
+ TEMPFN => '',
+ REGEX => '',
+ CAPTURE => 0,
+ @_
+ );
+ unless ($args{TEMPFN} && $args{REGEX}) {
+ script_error 'grok_temp_file requires two arguments';
+ }
+ my $out;
+ my $fh = open_read $args{TEMPFN};
+ FIRST: while (my $line = <$fh>) {
+ if ($line =~ $args{REGEX}) {
+ $out = ($line =~ $args{REGEX})[$args{CAPTURE}];
+ last FIRST;
+ }
+ }
+ close $fh;
+ return $out;
+}
+
+# wrappers around grok_temp_file
+sub get_src_dir ($) {
+ exists $_[0] or script_error 'get_src_dir requires an argument';
+ return grok_temp_file (TEMPFN => shift, REGEX => qr#^([^/]+)/#);
+}
+
+sub get_pkg_name ($) {
+ exists $_[0] or script_error 'get_pkg_name requires an argument';
+ return grok_temp_file (TEMPFN => shift,
+ REGEX => qr/^Slackware\s+package\s+([^\s]+)\s+created\.$/);
+}
+
+# prep and run .SlackBuild
+sub perform_sbo {
+ my %args = (
+ OPTS => 0,
+ JOBS => 0,
+ LOCATION => '',
+ ARCH => '',
+ C32 => 0,
+ X32 => 0,
+ @_
+ );
+ unless ($args{LOCATION} && $args{ARCH}) {
+ script_error 'perform_sbo requires LOCATION and ARCH.';
+ }
+ my $location = $args{LOCATION};
+ my $sbo = get_sbo_from_loc $location;
+ my ($cmd, %changes);
+ $changes{make} = "-j $args{JOBS}" if $args{JOBS};
+ if ($args{ARCH} eq 'x86_64' and ($args{C32} || $args{X32}) ) {
+ if ($args{C32}) {
+ $changes{libdirsuffix} = '';
+ } elsif ($args{X32}) {
+ $changes{arch_out} = 'i486';
+ }
+ $cmd = ". /etc/profile.d/32dev.sh &&";
+ }
+ $cmd .= "/bin/sh $location/$sbo.SlackBuild";
+ $cmd = "$args{OPTS} $cmd" if $args{OPTS};
+ my ($tempfh, $tempfn) = tempfile (DIR => $tempdir);
+ close $tempfh;
+ rewrite_slackbuild "$location/$sbo.SlackBuild", $tempfn, %changes;
+ chdir $location, my $out = system $cmd;
+ revert_slackbuild "$location/$sbo.SlackBuild";
+ die unless $out == 0;
+ my $src = get_src_dir $tempfn;
+ my $pkg = get_pkg_name $tempfn;
+ unlink $tempfn;
+ return $pkg, $src;
+}
+
+# "public interface", sort of thing.
+sub do_slackbuild {
+ my %args = (
+ OPTS => 0,
+ JOBS => 0,
+ LOCATION => '',
+ COMPAT32 => 0,
+ @_
+ );
+ $args{LOCATION} or script_error 'do_slackbuild requires LOCATION.';
+ my $location = $args{LOCATION};
+ my $sbo = get_sbo_from_loc $location;
+ my $arch = get_arch;
+ my $multi = check_multilib;
+ my $version = get_sbo_version $location;
+ my $x32;
+ if ($args{COMPAT32}) {
+ die "compat32 only works on x86_64.\n" unless $arch eq 'x86_64';
+ die "compat32 requires multilib.\n" unless $multi;
+ die "compat32 requires /usr/sbin/convertpkg-compat32.\n"
+ unless -f '/usr/sbin/convertpkg-compat32';
+ } else {
+ if ($arch eq 'x86_64') {
+ $x32 = check_x32 $args{LOCATION};
+ if ($x32 && ! $multi) {
+ die "$sbo is 32-bit which requires multilib on x86_64.\n";
+ }
+ }
+ }
+ my %downloads = get_sbo_downloads (
+ LOCATION => $location,
+ 32 => $args{COMPAT32}
+ );
+ my @symlinks = create_symlinks $args{LOCATION}, %downloads;
+ my ($pkg, $src) = perform_sbo (
+ OPTS => $args{OPTS},
+ JOBS => $args{JOBS},
+ LOCATION => $location,
+ ARCH => $arch,
+ C32 => $args{COMPAT32},
+ X32 => $x32,
+ );
+ if ($args{COMPAT32}) {
+ my ($tempfh, $tempfn) = tempfile (DIR => $tempdir);
+ close $tempfh;
+ my $cmd = "/usr/sbin/convertpkg-compat32 -i $pkg -d /tmp | tee $tempfn";
+ system ($cmd) == 0 or die;
+ unlink $pkg;
+ $pkg = get_pkg_name $tempfn;
+ }
+ unlink $_ for @symlinks;
+ return $version, $pkg, $src;
+}
+
+# remove work directories (source and packaging dirs under /tmp/SBo)
+sub make_clean ($$$) {
+ exists $_[2] or script_error 'make_clean requires three arguments.';
+ my ($sbo, $src, $version) = @_;
+ print "Cleaning for $sbo-$version...\n";
+ my $tmpsbo = "/tmp/SBo";
+ remove_tree ("$tmpsbo/$src") if -d "$tmpsbo/$src";
+ remove_tree ("$tmpsbo/package-$sbo") if -d "$tmpsbo/package-$sbo";
+ return 1;
+}
+
+# remove distfiles
+sub make_distclean {
+ my %args = (
+ SRC => '',
+ VERSION => '',
+ LOCATION => '',
+ @_
+ );
+ unless ($args{SRC} && $args{VERSION} && $args{LOCATION}) {
+ script_error 'make_distclean requires four arguments.';
+ }
+ my $sbo = get_sbo_from_loc $args{LOCATION};
+ make_clean $sbo, $args{SRC}, $args{VERSION};
+ print "Distcleaning for $sbo-$args{VERSION}...\n";
+ # remove any distfiles for this particular SBo.
+ my %downloads = get_sbo_downloads (LOCATION => $args{LOCATION});
+ for my $key (keys %downloads) {
+ my $filename = get_filename_from_link $key;
+ unlink $filename if -f $filename;
+ }
+ return 1;
+}
+
+# run upgradepkg for a created package
+sub do_upgradepkg ($) {
+ exists $_[0] or script_error 'do_upgradepkg requires an argument.';
+ system ('/sbin/upgradepkg', '--reinstall', '--install-new', shift);
+ return 1;
+}
diff --git a/t/do_tests.sh b/t/do_tests.sh
new file mode 100755
index 0000000..54222db
--- /dev/null
+++ b/t/do_tests.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+
+./prep.pl
+./test.t
diff --git a/t/prep.pl b/t/prep.pl
new file mode 100755
index 0000000..e2fe9bf
--- /dev/null
+++ b/t/prep.pl
@@ -0,0 +1,46 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings FATAL => 'all';
+use File::Copy;
+use Tie::File;
+
+chomp (my $pwd = `pwd`);
+mkdir "$pwd/SBO" unless -d "$pwd/SBO";
+copy ('/home/d4wnr4z0r/projects/sbotools/SBO-Lib/lib/SBO/Lib.pm', "$pwd/SBO");
+my @subs;
+open my $file_h, '<', "$pwd/SBO/Lib.pm";
+my $regex = qr/^sub\s+([^\s]+)\s+/;
+while (my $line = <$file_h>) {
+ if (my $sub = ($line =~ $regex)[0]) {
+ push @subs, $sub;
+ }
+}
+
+seek $file_h, 0, 0;
+my @not_exported;
+FIRST: for my $sub (@subs) {
+ my $found = 'FALSE';
+ my $has = 'FALSE';
+ SECOND: while (my $line = <$file_h>) {
+ if ($found eq 'FALSE') {
+ $found = 'TRUE', next SECOND if $line =~ /\@EXPORT/;
+ } else {
+ last SECOND if $line =~ /^\);$/;
+ $has = 'TRUE', last SECOND if $line =~ /$sub/;
+ }
+ }
+ push @not_exported, $sub unless $has eq 'TRUE';
+ seek $file_h, 0, 0;
+}
+
+close $file_h;
+tie my @file, 'Tie::File', "$pwd/SBO/Lib.pm";
+FIRST: for my $line (@file) {
+ if ($line =~ /\@EXPORT/) {
+ $line = "our \@EXPORT = qw(". join ' ', @not_exported;
+ }
+ $line = "#$line" if $line =~ /root privileges/;
+}
+
+
diff --git a/t/test.t b/t/test.t
new file mode 100755
index 0000000..71eca1d
--- /dev/null
+++ b/t/test.t
@@ -0,0 +1,95 @@
+#!/usr/bin/perl -I/home/d4wnr4z0r/projects/sbotools/t
+
+use 5.16.0;
+use strict;
+use warnings FATAL => 'all';
+use File::Temp qw(tempdir tempfile);
+use Test::More tests => 39;
+use SBO::Lib;
+
+ok (defined $SBO::Lib::tempdir, '$tempdir is defined');
+
+my $fh = open_read ('/home/d4wnr4z0r/projects/sbotools/t/test.t');
+ok (ref ($fh) eq 'GLOB', 'open_read works');
+close $fh;
+
+ok ($SBO::Lib::config{DISTCLEAN} eq 'FALSE', 'config{DISTCLEAN} is good');
+ok ($SBO::Lib::config{JOBS} == 2, 'config{JOBS} is good');
+ok ($SBO::Lib::config{NOCLEAN} eq 'TRUE', 'config{NOCLEAN} is good');
+ok ($SBO::Lib::config{PKG_DIR} eq 'FALSE', 'config{PKG_DIR} is good');
+ok ($SBO::Lib::config{SBO_HOME} eq '/usr/sbo', 'config{SBO_HOME} is good');
+
+ok (show_version == 1, 'show_version is good');
+ok (get_slack_version eq '14.0', 'get_slack_version is good');
+ok (chk_slackbuilds_txt == 1, 'check_slackbuilds_txt is good');
+#ok (rsync_sbo_tree == 1, 'rsync_sbo_tree is good');
+#ok (update_tree == 1, 'update_tree is good');
+ok (slackbuilds_or_fetch == 1, 'slackbuilds_or_fetch is good');
+
+print "pseudo-random sampling of get_installed_sbos output...\n";
+my $installed = get_installed_sbos;
+for my $key (keys @$installed) {
+ is ($$installed[$key]{version}, '1.13') if $$installed[$key]{name} eq 'OpenAL';
+ is ($$installed[$key]{version}, '9.5.1_enu') if $$installed[$key]{name} eq 'adobe-reader';
+ is ($$installed[$key]{version}, '4.1.3') if $$installed[$key]{name} eq 'libdvdnav';
+ is ($$installed[$key]{version}, '0.8.8.4') if $$installed[$key]{name} eq 'libmodplug';
+ is ($$installed[$key]{version}, '3.12.4') if $$installed[$key]{name} eq 'mozilla-nss';
+ is ($$installed[$key]{version}, '2.5.0') if $$installed[$key]{name} eq 'zdoom';
+}
+print "completed pseudo-random testing of get_installed_sbos \n";
+
+is (get_sbo_location 'nginx', '/usr/sbo/network/nginx', 'get_sbo_location is good');
+
+my $updates = get_available_updates;
+for my $key (keys @$updates) {
+ is ($$updates[$key]{installed}, '1.15', '$$updates[$key]{installed} good for mutagen') if $$updates[$key]{name} eq 'mutagen';
+ is ($$updates[$key]{update}, '1.20', '$$updates[$key]{update} good for mutagen') if $$updates[$key]{name} eq 'mutagen';
+}
+
+ok (get_arch eq 'x86_64', 'get_arch is good');
+
+my %dl_info = get_download_info (LOCATION => '/usr/sbo/system/wine', X64 => 0);
+my $link = 'http://downloads.sf.net/wine/source/1.4/wine-1.4.1.tar.bz2';
+is ($dl_info{$link}, '0c28702ed478df7a1c097f3a9c4cabd6', 'get_download_info test 01 good.');
+$link = 'http://www.unrealize.co.uk/source/dibeng-max-2010-11-12.zip';
+is ($dl_info{$link}, '97159d77631da13952fe87e846cf1f3b', 'get_download_info test 02 good.');
+
+%dl_info = get_sbo_downloads (LOCATION => '/usr/sbo/system/wine');
+$link = 'http://downloads.sf.net/wine/source/1.4/wine-1.4.1.tar.bz2';
+is ($dl_info{$link}, '0c28702ed478df7a1c097f3a9c4cabd6', 'get_sbo_downloads test 01 good.');
+$link = 'http://www.unrealize.co.uk/source/dibeng-max-2010-11-12.zip';
+is ($dl_info{$link}, '97159d77631da13952fe87e846cf1f3b', 'get_sbo_downloads test 02 good.');
+
+my %downloads = get_sbo_downloads (LOCATION => '/usr/sbo/system/ifuse');
+$link = 'http://www.libimobiledevice.org/downloads/ifuse-1.1.1.tar.bz2';
+is ($downloads{$link}, '8d528a79de024b91f12f8ac67965c37c', 'get_sbo_downloads test 03 good.');
+
+is (get_filename_from_link 'http://www.libimobiledevice.org/downloads/ifuse-1.1.1.tar.bz2', '/usr/sbo/distfiles/ifuse-1.1.1.tar.bz2', 'get_file_from_link good');
+is (compute_md5sum '/usr/sbo/distfiles//laptop-mode-tools_1.61.tar.gz', '6685af5dbb34c3d51ca27933b58f484e', 'compute_md5sum good');
+is ((verify_distfile '/usr/sbo/distfiles/laptop-mode-tools_1.61.tar.gz', '6685af5dbb34c3d51ca27933b58f484e'), 1, 'verify_distfile good');
+is (get_sbo_version '/usr/sbo/system/wine', '1.4.1', 'get_sbo_version good');
+is ((get_symlink_from_filename '/usr/sbo/distfiles/laptop-mode-tools_1.61.tar.gz', '/usr/sbo/system/laptop-mode-tools'), '/usr/sbo/system/laptop-mode-tools/laptop-mode-tools_1.61.tar.gz', 'get_symlink_from_filename good');
+ok (check_x32 '/usr/sbo/system/wine', 'check_x32 true for 32-bit only wine');
+ok (!(check_x32 '/usr/sbo/system/ifuse'), 'check_x32 false for not-32-bit-only ifuse');
+ok (check_multilib, 'check_multilib good');
+
+# TODO: find a way to write a test for rewrite_slackbuild, revert_slackbuild.
+
+%downloads = get_sbo_downloads (LOCATION => '/usr/sbo/system/wine', 32 => 1);
+my @symlinks = create_symlinks '/usr/sbo/system/wine', %downloads;
+is ($symlinks[0], '/usr/sbo/system/wine/wine-1.4.1.tar.bz2', '$symlinks[0] good for create_symlinks');
+is ($symlinks[1], '/usr/sbo/system/wine/dibeng-max-2010-11-12.zip', '$symlinks[1] good for create_symlinks');
+
+my $tempdir = tempdir (CLEANUP => 1);
+my $tempfh = tempfile (DIR => $tempdir);
+my $lmt = 'laptop-mode-tools_1.60';
+print {$tempfh} "$lmt/COPYING\n";
+print {$tempfh} "$lmt/Documentation/\n";
+print {$tempfh} "$lmt/README\n";
+print {$tempfh} "Slackware package skype-2.2.0.35-i486-1_SBo.tgz created.\n";
+#close $tempfh;
+is (get_src_dir $tempfh, 'laptop-mode-tools_1.60', 'get_src_dir good');
+is (get_pkg_name $tempfh, 'skype-2.2.0.35-i486-1_SBo.tgz', 'get_pkg_name good');
+%downloads = get_sbo_downloads (LOCATION => '/usr/sbo/system/wine', 32 => 1);
+is ((check_distfiles %downloads), 1, 'check_distfiles good');
+#is (do_convertpkg ($package), "$package-compat32", 'do_convertpkg good');
diff --git a/t/test.t~ b/t/test.t~
new file mode 100755
index 0000000..9b0a256
--- /dev/null
+++ b/t/test.t~
@@ -0,0 +1,95 @@
+#!/usr/bin/perl -I/home/d4wnr4z0r/projects/sbotools/t
+
+use 5.16.0;
+use strict;
+use warnings FATAL => 'all';
+use File::Temp qw(tempdir tempfile);
+use Test::More tests => 39;
+use SBO::Lib;
+
+ok (defined $SBO::Lib::tempdir, '$tempdir is defined');
+
+my $fh = open_read ('/home/d4wnr4z0r/projects/sbotools/t/test.t');
+ok (ref ($fh) eq 'GLOB', 'open_read works');
+close $fh;
+
+ok ($SBO::Lib::config{DISTCLEAN} eq 'FALSE', 'config{DISTCLEAN} is good');
+ok ($SBO::Lib::config{JOBS} == 2, 'config{JOBS} is good');
+ok ($SBO::Lib::config{NOCLEAN} eq 'TRUE', 'config{NOCLEAN} is good');
+ok ($SBO::Lib::config{PKG_DIR} eq 'FALSE', 'config{PKG_DIR} is good');
+ok ($SBO::Lib::config{SBO_HOME} eq '/usr/sbo', 'config{SBO_HOME} is good');
+
+ok (show_version == 1, 'show_version is good');
+ok (get_slack_version eq '14.0', 'get_slack_version is good');
+ok (chk_slackbuilds_txt == 1, 'check_slackbuilds_txt is good');
+#ok (rsync_sbo_tree == 1, 'rsync_sbo_tree is good');
+#ok (update_tree == 1, 'update_tree is good');
+ok (slackbuilds_or_fetch == 1, 'slackbuilds_or_fetch is good');
+
+print "pseudo-random sampling of get_installed_sbos output...\n";
+my $installed = get_installed_sbos;
+for my $key (keys @$installed) {
+ is ($$installed[$key]{version}, '1.13') if $$installed[$key]{name} eq 'OpenAL';
+ is ($$installed[$key]{version}, '9.5.1_enu') if $$installed[$key]{name} eq 'adobe-reader';
+ is ($$installed[$key]{version}, '4.1.3') if $$installed[$key]{name} eq 'libdvdnav';
+ is ($$installed[$key]{version}, '0.8.8.4') if $$installed[$key]{name} eq 'libmodplug';
+ is ($$installed[$key]{version}, '3.12.4') if $$installed[$key]{name} eq 'mozilla-nss';
+ is ($$installed[$key]{version}, '2.5.0') if $$installed[$key]{name} eq 'zdoom';
+}
+print "completed pseudo-random testing of get_installed_sbos \n";
+
+is (get_sbo_location 'nginx', '/usr/sbo/network/nginx', 'get_sbo_location is good');
+
+my $updates = get_available_updates;
+for my $key (keys @$updates) {
+ is ($$updates[$key]{installed}, '1.15', '$$updates[$key]{installed} good for mutagen') if $$updates[$key]{name} eq 'mutagen';
+ is ($$updates[$key]{update}, '1.20', '$$updates[$key]{update} good for mutagen') if $$updates[$key]{name} eq 'mutagen';
+}
+
+ok (get_arch eq 'x86_64', 'get_arch is good');
+
+my %dl_info = get_download_info (LOCATION => '/usr/sbo/system/wine', X64 => 0);
+my $link = 'http://downloads.sf.net/wine/source/1.4/wine-1.4.1.tar.bz2';
+is ($dl_info{$link}, '0c28702ed478df7a1c097f3a9c4cabd6', 'get_download_info test 01 good.');
+$link = 'http://www.unrealize.co.uk/source/dibeng-max-2010-11-12.zip';
+is ($dl_info{$link}, '97159d77631da13952fe87e846cf1f3b', 'get_download_info test 02 good.');
+
+%dl_info = get_sbo_downloads (LOCATION => '/usr/sbo/system/wine');
+$link = 'http://downloads.sf.net/wine/source/1.4/wine-1.4.1.tar.bz2';
+is ($dl_info{$link}, '0c28702ed478df7a1c097f3a9c4cabd6', 'get_sbo_downloads test 01 good.');
+$link = 'http://www.unrealize.co.uk/source/dibeng-max-2010-11-12.zip';
+is ($dl_info{$link}, '97159d77631da13952fe87e846cf1f3b', 'get_sbo_downloads test 02 good.');
+
+my %downloads = get_sbo_downloads (LOCATION => '/usr/sbo/system/ifuse');
+$link = 'http://www.libimobiledevice.org/downloads/ifuse-1.1.1.tar.bz2';
+is ($downloads{$link}, '8d528a79de024b91f12f8ac67965c37c', 'get_sbo_downloads test 03 good.');
+
+is (get_filename_from_link 'http://www.libimobiledevice.org/downloads/ifuse-1.1.1.tar.bz2', '/usr/sbo/distfiles/ifuse-1.1.1.tar.bz2', 'get_file_from_link good');
+is (compute_md5sum '/usr/sbo/distfiles//laptop-mode-tools_1.61.tar.gz', '6685af5dbb34c3d51ca27933b58f484e', 'compute_md5sum good');
+is ((verify_distfile '/usr/sbo/distfiles/laptop-mode-tools_1.61.tar.gz', '6685af5dbb34c3d51ca27933b58f484e'), 1, 'verify_distfile good');
+is (get_sbo_version '/usr/sbo/system/wine', '1.4.1', 'get_sbo_version good');
+is ((get_symlink_from_filename '/usr/sbo/distfiles/laptop-mode-tools_1.61.tar.gz', '/usr/sbo/system/laptop-mode-tools'), '/usr/sbo/system/laptop-mode-tools/laptop-mode-tools_1.61.tar.gz', 'get_symlink_from_filename good');
+ok (check_x32 '/usr/sbo/system/wine', 'check_x32 true for 32-bit only wine');
+ok (!(check_x32 '/usr/sbo/system/ifuse'), 'check_x32 false for not-32-bit-only ifuse');
+ok (check_multilib, 'check_multilib good');
+
+# TODO: find a way to write a test for rewrite_slackbuild, revert_slackbuild.
+
+%downloads = get_sbo_downloads (LOCATION => '/usr/sbo/system/wine', 32 => 1);
+my @symlinks = create_symlinks '/usr/sbo/system/wine', %downloads;
+is ($symlinks[0], '/usr/sbo/system/wine/wine-1.4.1.tar.bz2', '$symlinks[0] good for create_symlinks');
+is ($symlinks[1], '/usr/sbo/system/wine/dibeng-max-2010-11-12.zip', '$symlinks[1] good for create_symlinks');
+
+my $tempdir = tempdir (CLEANUP => 1);
+my $tempfh = tempfile (DIR => $tempdir);
+my $lmt = 'laptop-mode-tools_1.60';
+print {$tempfh} "$lmt/COPYING\n";
+print {$tempfh} "$lmt/Documentation/\n";
+print {$tempfh} "$lmt/README\n";
+print {$tempfh} "Slackware package skype-2.2.0.35-i486-1_SBo.tgz created.\n";
+#close $tempfh;
+is (get_src_dir $tempfh, 'laptop-mode-tools_1.60', 'get_src_dir good');
+is (get_pkg_name $tempfh, 'skype-2.2.0.35-i486-1_SBo.tgz', 'get_pkg_name good');
+%downloads = get_sbo_downloads (LOCATION => '/usr/sbo/system/wine', 32 => 1);
+is (check_distfiles %downloads, 1, 'check_distfiles good');
+#is (do_convertpkg ($package), "$package-compat32", 'do_convertpkg good');