#!/usr/local/bin/perl
#	qp -- "Quoted printable" decoder/encoder
#	Brian Katzung	9 February 1996
#
# This software is provided on a strictly as-is basis.
# Use it at your own risk.
#
# Options:
#	-d	Decode (default)
#	-e	Encode
#	-l	Quote line breaks (\r, \n)
#	-m	Quote mail constructs (., From )
#	-t	Quote tabs (\t)
#	-u	Quote unsafe (most punctuation, etc.)

$True = 1;
$False = 0;

######################################################################
# Decode printed-quotable input
sub Decode
{
	local(@Parts);

	while (<>)
	{
		s/[ \t]+(\r?\n)/\1/;	# Strip trailing white space
		s/=\r?\n//;		# Strip soft line breaks
		# Split the line at quoted characters
		# (s/pattern/expression/eg leaks in Perl 4)
		@Parts = split(/(=[0-9A-F][0-9A-F])/);
		for (@Parts)
		{
			# Decode quoted characters
			$_ = pack('c', hex($1)) if /^=([0-9A-F][0-9A-F])$/;
		}
		# All the King's horses and all the King's men...
		print join('', @Parts);
	}
}

######################################################################
# Encode input as quoted-printable
sub Encode
{
	local(@Parts);
	local($Term);

	# Part of soft line breaks when real breaks are encoded
	$Term = "\n" if $QuoteBreaks == $True;

	while (<>)
	{
		# Split the line into text and termination
		($_, $Term) = /([^\r\n]*)([\r\n]*)/
		  unless $QuoteBreaks == $True;

		# Quote "unsafe" characters
		# (avoid Perl 4 s///eg leak as above)
		if ($QuoteUnsafe == $True)
		{
			@Parts = split(/([^ A-Za-z0-9'()+,-.\/:?])/);
		}
		else
		{
			@Parts = split(/([^ -<>-~])/);
		}
		for (@Parts)
		{
			if ($QuoteUnsafe == $True?
			  ($_ =~ /[^ A-Za-z0-9'()+,-.\/:?]/):
			  ($_ =~ /[^ -<>-~]/))
			{
				$_ = sprintf("=%02X", ord()) unless
				  $_ eq "\t" && $QuoteTabs == $False;
			}
		}
		$_ = join('', @Parts);

		# Quote a trailing blank or tab to avoid losing them
		# at gateways
		s/\t$/=09/;
		s/ $/=20/;

		# Quote things that sometimes trouble mail, if requested
		if ($QuoteMail == $True)
		{
			s/^From /=46rom /;
			s/^\.((=0A|=0D)*)$/=2E\1/;
		}

		# Real line breaks are encoded, so add a soft line break
		$_ .= '=' if $QuoteBreaks == $True;

		# Output the line in pieces no longer than 76 characters
		while (length() > 76)
		{
			# Grab 73 to 75 characters such that neither of
			# the last two contain the start of an encoded
			# character, and append a soft line break
			/^(.{71,73}[^=][^=])(.*)/;
			print "$1=\n";

			# Try again with the remainder of the line
			$_ = $2;
		}
		print "$_$Term";
	}
}

######################################################################
# Main program

$Mode = 'decode';
$QuoteBreaks = $False;
$QuoteMail = $False;
$QuoteTabs = $False;
$QuoteUnsafe = $False;

while ($ARGV[0] =~ /^-/)
{
	$Arg = shift;
	last if $Arg eq '--';
	$Mode = 'decode' if $Arg =~ /d/;
	$Mode = 'encode' if $Arg =~ /e/;
	$QuoteBreaks = $True if $Arg =~ /l/;	# Quote \r and \n
	$QuoteMail = $True if $Arg =~ /m/;	# Quote "^From " and "^.$"
	$QuoteTabs = $True if $Arg =~ /t/;	# Quote \t
	$QuoteUnsafe = $True if $Arg =~ /u/;	# Quote most punctuation, etc.
}

if ($Mode eq 'encode')
{
	&Encode;
}
else
{
	&Decode;
}
