#!/usr/local/bin/perl

# 
# b64.pl: Base64 の encode/decode
# 
#     [1999/05/24] OSHIRO Naoki.
# 
#     $Log:$
#

# see.
#   Newsgroups: fj.comp.lang.c
#   From: manmos@stellar.co.jp (Hideo "Sir MaNMOS" Morishita)
#   Subject: Re: Base64Encode-Decode
#   Message-ID: <squbtqzztt7.fsf@stellar.co.jp>

require "getopts.pl";
&Getopts("d"); # d:decode

if ($#ARGV>-1) {
    open(F, "<$ARGV[0]");
    $fid=F;
} else {
    $fid=STDIN;
}

@b64str=split('',"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");

unless ($opt_d) {
    $buflen=300*3; # 3 の倍数にする
    $num=0;
    while (!eof($fid)) {
	$len=read($fid, $dat, $buflen);
	@dat=unpack('C*', $dat);
	while (@dat) {
	    $a=shift(@dat);
	    $b=shift(@dat);
	    $c=shift(@dat);
	    if ($b eq "") {
		($d,$e)=($a>>2, ($a<<4)&0x3f);
		print "$b64str[$d]$b64str[$e]==";
	    } elsif ($c eq "") {
		($d,$e,$f)=(($a>>2), ((($a<<4)&0x3f) | $b>>4), ($b<<2)&0x3f);
		print "$b64str[$d]$b64str[$e]$b64str[$f]=";
	    } else {
		($d,$e,$f,$g)=
		    ($a>>2,
		     (($a<<4)&0x3f) | $b>>4,
		     ($b<<2)&0x3f | $c>>6,
		     $c&0x3f);
		print "$b64str[$d]$b64str[$e]$b64str[$f]$b64str[$g]";
	    }
	    if (++$num==15) {print "\n"; $num=0;}
	}
    }
    print "\n" unless ($num==0);
} else {
    $buflen=300*4; # 4 の倍数にする
    $i=0;
    foreach $b (@b64str) {
	$b64str{$b}=$i;
	$i++;
    }
    while (!eof($fid)) {
	$len=read($fid, $dat, $buflen);
	@dat=split('', $dat);
	PUTDATA: while (@dat) {
	    $a=shift(@dat);
	    while ($a eq "\n" || $a eq "") {
		if (!eof($fid)) {
		    read($fid, $dat, 1);
		    push(@dat,$dat);
		} else {
		    last PUTDATA if ($#dat<0);
		}
		redo PUTDATA;
	    }
	    $b=shift(@dat);
	    $c=shift(@dat);
	    $d=shift(@dat);
	    $aa=$b64str{$a};
	    $bb=$b64str{$b};
	    $cc=$b64str{$c};
	    $dd=$b64str{$d};
	    if ($a eq "=") {
	    } elsif ($b eq "=") {
		printf "%c", ($aa<<2);
	    } elsif ($c eq "=") {
		printf "%c", ($aa<<2|$bb>>4);
	    } elsif ($d eq "=") {
		printf "%c%c", ($aa<<2|$bb>>4), ($bb<<4|$cc>>2);
	    } else {
		printf "%c%c%c", ($aa<<2|$bb>>4), ($bb<<4|$cc>>2), ($cc<<6|$dd);
	    }
	}
    }
}
close($fid);
