#!/usr/bin/perl -w
#<b64: encode/decode a base-64 file

use strict;
use subs qw/manpage myuuid usage version where/;
use MIME::Base64 qw/decode_base64 encode_base64/;
use IO::File;
use Getopt::Long;
use Pod::Usage;
use File::Basename;
use Carp;

$ENV{'PATH'} = join ":", qw(/bin /usr/bin /usr/local/bin /opt/sfw/bin);

my $myname = basename($0);
$myname =~ s/\.\w*$//;   # strip any extension

my %options;
my @getopt_args = (
    'd',      # decode?
    'h|?',    # print usage
    'm',      # print manpage
    'u',      # print UUID
    'v',      # print version
    'w',      # print source location
    );

Getopt::Long::config("noignorecase", "bundling");
usage unless GetOptions(\%options, @getopt_args);

manpage if $options{'m'};
myuuid  if $options{'u'};
version if $options{'v'};
where   if $options{'w'};
usage   if $options{'h'};

# If no files on the command line, stdin -> stdout.

unless (@ARGV) {
    if ($options{d}) {    ## Decode
        print decode_base64($_) while <STDIN>;
    } else {    ## Encode
        my $buf;
        binmode(STDIN);
        print encode_base64($buf) while read(STDIN, $buf, 60 * 57);
    }
    exit(0);
}

# Either encode or decode each file on the command line.

my ($fname, $oname, $ifh, $ofh);
my $k = 0;

foreach $fname (@ARGV) {
    $ifh = new IO::File;

    if ($options{d}) {    ## Decode
        $ofh = new IO::File;
        $k++;
        $oname = sprintf ("%4.4d.out", $k);

        if ($ifh->open("< $fname")) {
            $_ = <$ifh>;
            s/begin //;
            $_ = decode_base64($_);
            print STDERR "decoding $fname\n";

            if ($ofh->open("> $oname")) {
                while (<$ifh>) {
                }
                $ofh->close;
            }
            else {
                croak "can't write $_\n";
            }
            $ifh->close;
        }
        else {
            carp "$fname: cannot read: $!\n";
        }
    }

    else {    ## Encode
        my $buf;

        if ($ifh->open("< $fname")) {
            print "begin ", encode_base64($fname);
            binmode($ifh);

            while (read($ifh, $buf, 60 * 57)) {
                print encode_base64($buf);
            }
            $ifh->close;
        }
        else {
            carp "$fname: cannot read: $!\n";
        }
    }
}

exit(0);

#---------------------------------------------------------------------
# Print a usage message from the comments and exit.

sub usage {
    my ($emsg) = @_;

    use Pod::Usage qw(pod2usage);
    warn "$emsg\n" if defined $emsg;
    pod2usage(-verbose => 99, -sections => "NAME|SYNOPSIS|OPTIONS");
}

sub manpage {
    my @args = ("perldoc", "$0");
    exec { $args[0] } @args;          # safe even with one-arg list
    die "should not get here\n";
}

#---------------------------------------------------------------------
# Print the UUID, current version, or source location.

sub myuuid {
    my $UUID = $1
        if q$UUID: f379e87e-d110-376b-b4a4-4317f6e765e6 $ =~ /UUID: (.*) /;
    print "$UUID\n";
    exit(0);
}

sub version {
    my $VERSION = sprintf("%d.%02d", q$Revision: 1.8 $ =~ /(\d+)\.(\d+)/);
    my $DATE = $1 if q$Date: 2012-07-26 17:11:47-04 $ =~ /Date: (.*) /;
    print "$myname $VERSION $DATE\n";
    exit(0);
}

sub where {
    my $SOURCE = $1
        if q$Source: /home/vogelke/bin/RCS/b64,v $ =~
          /Source: (.*) /;
    my $HOST = $1 if q$Host: sys7.com $ =~ /Host: (.*) /;
    print "file://$HOST", "$SOURCE\n";
    exit(0);
}

#---------------------------------------------------------------------
__END__

=head1 NAME

b64 - encode/decode base-64 file.

=head1 SYNOPSIS

b64 [-dhmuvw] file [file ...]

=head1 OPTIONS

=over 4

=item B<-d>

Input file is base-64 encoded, write an unencoded file with
a generated filename.

=item B<-h>

Print a brief help message and exit.

=item B<-m>

Print the manual page and exit.

=item B<-u>

Print the script UUID and exit.

=item B<-v>

Print the version and exit.

=item B<-w>

Print the source location and exit.

=back

=head1 DESCRIPTION

B<b64> will read the given input file(s) and either write them
as base-64 or decode them from base-64.  Default is to encode.

=head1 AUTHOR

 Karl Vogel <vogelke@pobox.com>
 Oasis Systems, Inc.

=cut
