#!/usr/pkg/bin/perl

=begin metadata

Name: shar
Description: create a shell archive of files
Author: Rich Salz, salzr@certo.com, rsalz@osf.org, rsalz@bbn.com
License: public domain

=end metadata

=cut

use strict;

use File::Basename qw(basename);
use Getopt::Std qw(getopts);

use constant EX_SUCCESS => 0;
use constant EX_FAILURE => 1;

my $Program = basename($0);

getopts('') or usage();
@ARGV or usage();

print '# --cut here--
# To extract, remove everything before the "cut here" line
# and run the command "sh file".
';

my $done = 0;
ARGUMENT: for my $f ( @ARGV ) {
    if (length($f) == 0) {
	warn "$Program: empty file name\n";
	next ARGUMENT;
    }
    my $quoted = quotefile($f);
    if (-d $f) {
	print "echo x - $quoted/\n";
	print "mkdir -p $quoted\n";
	$done++;
	next ARGUMENT;
    }
    my $fh;
    unless (open $fh, '<', $f) {
	warn "$Program: can't open '$f': $!\n";
	next ARGUMENT;
    }
    binmode $fh;

    print "echo x - $quoted\n";
    if (-B $f) {
	my $mode = (stat $f)[2];
	$mode = (join '', 0, ($mode&0700)>>6, ($mode&0070)>>3, ($mode&0007));
	print "uudecode <<'FUNKY_STUFF'\n";
	print "begin $mode $f\n";
	my $block;
	print pack 'u', $block while read $fh, $block, 45;
	print "`\nend\n";
    } else {
	print "sed -e 's/^X//' >$quoted <<'FUNKY_STUFF'\n";
	print 'X', $_ while ( <$fh> );
    }
    print "FUNKY_STUFF\n";
    unless (close $fh) {
	warn "$Program: can't close '$f': $!\n";
	next ARGUMENT;
    }
    $done++;
}
if ($done == 0) {
    warn "$Program: no input files were processed\n";
    exit EX_FAILURE;
}
exit EX_SUCCESS;

sub usage {
    warn "usage: $Program file...\n";
    exit EX_FAILURE;
}

sub quotefile {
    my $name = shift;
    if ($name =~ m/[\s\'\"\(\)\[\]\{\}\;\:\*]/) {
	$name =~ s/\"/\\\"/g;
	$name = "\"$name\"";
    }
    return $name;
}

__END__

=head1 NAME

shar - create a shell archive of files

=head1 SYNOPSIS

B<shar> file...

=head1 DESCRIPTION

B<shar> reads input files and writes a shell archive to standard output.
The shell archive is a shell script, and executing it will recreate the I<files>.
File permissions are not preserved for archived files.
Extracted files are created with the default file permissions and owner.
Directories will be recreated, but directory arguments must be provided before
the files they contain.

=head1 SEE ALSO

B<unshar>

=head1 AUTHOR

Rich Salz | salzr@certo.com | rsalz@osf.org | rsalz@bbn.com

=head1 COPYRIGHT

This code is released to the public domain.

=cut
