#!perl use strict; use warnings; use Getopt::Long; use File::Basename; use File::Spec; BEGIN { if ($^O eq 'VMS') { require VMS::Filespec; import VMS::Filespec; } } Getopt::Long::Configure('no_ignore_case'); our $LastUpdate = -M $0; sub handle_file { my $opts = shift; my $file = shift or die "Need file\n". usage(); my $outfile = shift || ''; $file = vms_check_name($file) if $^O eq 'VMS'; my $mode = (stat($file))[2] & 07777; open my $fh, "<", $file or do { warn "Could not open input file $file: $!"; exit 0 }; binmode $fh; my $str = do { local $/; <$fh> }; ### unpack? my $outstr; if( $opts->{u} ) { if( !$outfile ) { $outfile = $file; $outfile =~ s/\.packed\z//; } my ($head, $body) = split /__UU__\n/, $str; die "Can't unpack malformed data in '$file'\n" if !$head; $outstr = unpack 'u', $body; } else { $outfile ||= $file . '.packed'; my $me = basename($0); $outstr = <<"EOFBLURB" . pack 'u', $str; ######################################################################### This is a binary file that was packed with the 'uupacktool.pl' which is included in the Perl distribution. To unpack this file use the following command: $me -u $outfile $file To recreate it use the following command: $me -p $file $outfile Created at @{[scalar localtime]} ######################################################################### __UU__ EOFBLURB } ### output the file if( $opts->{'s'} ) { print STDOUT $outstr; } else { $outfile = VMS::Filespec::vmsify($outfile) if $^O eq 'VMS'; print "Writing $file into $outfile\n" if $opts->{'v'}; open my $outfh, ">", $outfile or do { warn "Could not open $outfile for writing: $!"; exit 0 }; binmode $outfh; ### $outstr might be empty, if the file was empty print $outfh $outstr if $outstr; close $outfh; chmod $mode, $outfile; } ### delete source file? if( $opts->{'D'} and $file ne $outfile ) { 1 while unlink $file; } } sub bulk_process { my $opts = shift; my $Manifest = $opts->{'m'}; open my $fh, "<", $Manifest or die "Could not open '$Manifest':$!"; print "Reading $Manifest\n" if $opts->{'v'}; my $count = 0; my $lines = 0; while( my $line = <$fh> ) { chomp $line; my ($file) = split /\s+/, $line; $lines++; next unless $file =~ /\.packed/; $count++; my $out = $file; $out =~ s/\.packed\z//; $out = vms_check_name($out) if $^O eq 'VMS'; ### unpack if( !$opts->{'c'} ) { ( $out, $file ) = ( $file, $out ) if $opts->{'p'}; if (-e $out) { my $changed = -M _; if ($changed < $LastUpdate and $changed < -M $file) { print "Skipping '$file' as '$out' is up-to-date.\n" if $opts->{'v'}; next; } } handle_file($opts, $file, $out); print "Converted '$file' to '$out'\n" if $opts->{'v'}; ### clean up } else { ### file exists? unless( -e $out ) { print "File '$file' was not unpacked into '$out'. Can not remove.\n"; ### remove it } else { print "Removing '$out'\n"; 1 while unlink $out; } } } print "Found $count files to process out of $lines in '$Manifest'\n" if $opts->{'v'}; } sub usage { return qq[ Usage: $^X $0 [-d dir] [-v] [-c] [-D] -p|-u [orig [packed|-s] | -m [manifest]] Handle binary files in source tree. Can be used to pack or unpack files individiually or as specified by a manifest file. Options: -u Unpack files (defaults to -u unless -p is specified) -p Pack files -c Clean up all unpacked files. Implies -m -D Delete source file after encoding/decoding -s Output to STDOUT rather than OUTPUT_FILE -m Use manifest file, if none is explicitly provided defaults to 'MANIFEST' -d Change directory to dir before processing -v Run verbosely -h Display this help message ]; } sub vms_check_name { # Packed files tend to have multiple dots, which the CRTL may or may not handle # properly, so convert to native format. And depending on how the archive was # unpacked, foo.bar.baz may be foo_bar.baz or foo.bar_baz. N.B. This checks for # existence, so is not suitable as-is to generate ODS-2-safe names in preparation # for file creation. my $file = shift; $file = VMS::Filespec::vmsify($file); return $file if -e $file; my ($vol,$dirs,$base) = File::Spec->splitpath($file); my $tmp = $base; 1 while $tmp =~ s/([^\.]+)\.(.+\..+)/$1_$2/; my $try = File::Spec->catpath($vol, $dirs, $tmp); return $try if -e $try; $tmp = $base; 1 while $tmp =~ s/(.+\..+)\.([^\.]+)/$1_$2/; $try = File::Spec->catpath($vol, $dirs, $tmp); return $try if -e $try; return $file; } my $opts = {}; GetOptions($opts,'u','p','c', 'D', 'm:s','s','d=s','v','h'); die "Can't pack and unpack at the same time!\n", usage() if $opts->{'u'} && $opts->{'p'}; die usage() if $opts->{'h'}; if ( $opts->{'d'} ) { chdir $opts->{'d'} or die "Failed to chdir to '$opts->{'d'}':$!"; } $opts->{'u'} = 1 if !$opts->{'p'}; binmode STDOUT if $opts->{'s'}; if ( exists $opts->{'m'} or exists $opts->{'c'} ) { $opts->{'m'} ||= "MANIFEST"; bulk_process($opts); exit(0); } else { if (@ARGV) { handle_file($opts, @ARGV); } else { die "No file to process specified!\n", usage(); } exit(0); } die usage();