Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Self-Extracting Perl Archive

by #include (Curate)
on Dec 29, 2003 at 00:40 UTC ( [id://317352]=sourcecode: print w/replies, xml ) Need Help??
Category: Utility Scripts
Author/Contact Info Dan Hetrick
dhetrick@riotmod.com
http://www.riotmod.com
Description: Takes a list of files and converts them into a Perl script that extracts the files when ran. Uses File::Basename and Digest::MD5. The script is printed to STDOUT, and requires nothing but Perl and Digest::MD5.

SUPER MAJOR UPDATE: Removed the GUI, and added file integrity checking with MD5 hashes.
#!/usr/bin/perl
#
# sepa.pl
#
# Self-Extracting Perl Archive
#
# Usage:
# sepa.pl file,file,...
#
# Creates a Perl script that contains
# a number of files;  when the script
# is executed, the files are extracted
# and written to the current directory
#
# File integrity is now checked, using
# Digest::MDS.  This is the ONLY requirement
# for the output script.  This comes with
# the base Perl installation, so it should not
# be a problem
#
use strict;
use File::Basename;
use Digest::MD5;

my $APPNAME = "sepa.pl";

my $VERSION = "0.2";

if ( $#ARGV >= 0 ) {
    my $script = MakeArchiveScript(@ARGV);
    print $script;
}
else {
    print "Self-Extracting Perl Archive $VERSION\n";
    print "Usage: $0 file,file,...\n";
    exit;
}

sub MakeArchiveScript {
    my (@file_list) = @_;
    my @stub=<DATA>;
    my $retval = join('',@stub);
    $retval=~s/!APPNAME/$APPNAME/g;
    $retval=~s/!VERSION/$VERSION/g;
    $retval .= MakePerlArchive(@file_list);
    return $retval;
}

# MakePerlArchive(@file_list)
#
# Takes an array of
# files as an argument, and returns
# a Perl script that will extract
# those files into the current
# directory
#
sub MakePerlArchive {
    my (@archive_list) = @_;
    my $packsubs       = "";
    my $retval         = "";
    my $hash           = "";
    foreach my $file (@archive_list) {
        my $original_filename = $file;
    $hash = HashFile($original_filename);
        my $outputfilename    = basename($file);
        my $subname = random_string(10);
        my $packedbin = PackBinaryFile( $original_filename, $subname )
+;
        $packsubs .= "$packedbin\n";
        $retval   .= '$file="' . $outputfilename . '";' . "\n";
    $retval   .= '$hash="' . $hash . '";' . "\n";
        $retval   .= '$packed_data=' . $subname . '();' . "\n";
        $retval   .=
          'open(FILE,">$file") || die "Error writing file - $!\n";' . 
+"\n";
        $retval .= 'binmode FILE;' . "\n";
        $retval .= 'print FILE $packed_data;' . "\n";
        $retval .= 'close FILE;' . "\n";
    $retval .= 'if(VerifyFile($hash,$file)==0) { print "$file is damag
+ed. Not extracted.\n"; unlink $file; } else { print "Extracted $file\
+n"; } ' . "\n";
    }
    $retval .= 'print "\n";' . "\n";
    $retval .= "$packsubs\n";
    return $retval;
}

#
# PackBinaryFile($filename,$subroutine_name)
#
# Loads a file, packs it, and makes a Perl
# subroutine to unpack it.
#
# Found on comp.lang.perl.misc in a post by
# Jonathan Stowe (gellyfish@gellyfish.com)
#
sub PackBinaryFile {
    my $file    = shift || die "$0: No file specified\n";
    my $subname = shift || die "$0: No subname specified\n";
    open( FILE, $file ) || die "Couldnt open $file - $!\n";
    binmode FILE;
    my $imgdata = do { local $/; <FILE> };
    my $uustring = pack "u", $imgdata;
    return <<EOSUB;
sub $subname
{
  return unpack "u", <<'EOIMG';
$uustring
EOIMG
}
EOSUB
}

#
# random_string($length)
#
# Creates a "random" string
# of the specified length
#
sub random_string {
    my $length = shift || 2;
    my @chars = ( 'a' .. 'z', 'A' .. 'Z' );
    join( '', map { $chars[ rand() * @chars ] } ( 1 .. $length ) );
}

sub HashFile
{
    my($filename)=@_;
    open( FILE, $filename ) || die "Couldnt open $filename - $!\n";
    binmode FILE;
    my $fdata = do { local $/; <FILE> };
    close FILE;
    my $md5 = Digest::MD5->new;
    $md5->add($fdata);
    return $md5->hexdigest;

}

__DATA__
#!/usr/bin/perl
use strict;
use Digest::MD5;

my $file;
my $packed_data;
my $hash;

sub VerifyFile
{
    my($ohash,$filename)=@_;
    if($ohash==HashFile($filename)) { return 1; }
    return 0;
}

sub HashFile
{
    my($filename)=@_;
    open( FILE, $filename ) || die "Couldnt open $filename - $!\n";
    binmode FILE;
    my $fdata = do { local $/; <FILE> };
    close FILE;
    my $md5 = Digest::MD5->new;
    $md5->add($fdata);
    return $md5->hexdigest;

}

print "\n********************************\n";
print "* Self Extracting Perl Archive *\n";
print "********************************\n\n";
print "Created with !APPNAME !VERSION\n\n";
Replies are listed 'Best First'.
Re: Self-Extracting Perl Archive
by belg4mit (Prior) on Dec 29, 2003 at 06:08 UTC
    See par. I've used this to get around the soon-to-be-fixed-for-a-real-long-time-now limitation on CPAN of not supporting archives for script uploads.

    --
    I'm not belgian but I play one on TV.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: sourcecode [id://317352]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having an uproarious good time at the Monastery: (6)
As of 2024-03-29 11:16 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found