#!/usr/bin/env perl
#
# Hinweis:	Tabulatorbreite: 4 Zeichen
# Notice:   tab width: 4 characters
#
########################################################################
#                                                                      #
#    Muttprint - pretty printing of mails with Mutt                    #
#    Copyright (c) 2000-04, Bernhard Walle <bernhard.walle@gmx.de>     #
#    Copyright (c) 2005,    Lukas Ruf <lukas.ruf@lpr.ch>               #
#                                                                      #
#    This program is free software; you can redistribute it and/or     #
#    modify it under the terms of the GNU General Public License as    #
#    published by the Free Software Foundation; either version 2 of    #
#    the License, or (at your option) any later version.               #
#                                                                      #
#    This program is distributed in the hope that it will be useful,   #
#    but WITHOUT ANY WARRANTY; without even the implied warranty of    #
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU  #
#    General Public License for more details.                          #
#                                                                      #
#    You should have received a copy of the GNU General Public License #
#    along with this program; if not, write to the Free Software       #
#    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.         #
#                                                                      #
#    You find the GPL in the file "COPYING" which was distributed      #
#    with Muttprint. For a German translation look at                  #
#                                                                      #
#    http://www.suse.de/de/private/support/licenses/gpl.html           #
#                                                                      #
########################################################################

# Deklaration erzwingen
use strict;

use vars qw(%Config $texFontenc %String);
use sigtrap qw(die INT QUIT TERM);
use POSIX;
use Text::Wrap;
use File::Temp qw(tempdir);
use utf8;

# boolean type
use constant TRUE 	=> 1;
use constant FALSE	=> 0;

use constant COUNTRYCODE 			=> 0;
use constant LANGUAGECODE			=> 1;
use constant CHARSET 				=> 2;
use constant CHARSET_TEX_NOTATION 	=> 3;
use constant BABEL_LANGUAGE 		=> 4;
use constant TEX_FONTENC			=> 5;


############# BEGIN VARIABLES ################################################


our $VERSION = "0.72d";
my $PACKAGE = "Muttprint";


######## Subroutinen mit Prototypes
sub readConfig (@);
sub readOpts ();
sub getPaperConfig ();
sub findCommonDir ($);
sub createLatex ();
sub getRealname ($);
sub getShortFrom($$);
sub getNumberOfPages ($);
sub copyFile ($$);
sub printDuplexNoCups ($$$$);
sub printDuplexCups ($$$);
sub modifyPS ($);
sub createTemp ();
sub setStrings ();
sub writeFormated ($*$);
sub decodeHeader();
sub getDefaultPrinterCDE();
sub changeForXface($);
sub convertDate($);
sub getISOlatinExtensions($);
sub fatalError($);
sub printWarning($);
sub output (*@);
sub outputstd (@);
sub printLog($);
sub input (*);
sub native2utf ($);
sub utf2native ($);
sub getLocaleInformation($);
sub convert_init ();

######## 'private' variables for the rest
%Config = (
	PRINTER			=> 	'',
	PRINT_COMMAND	=>	'lpr -P$PRINTER',
	PENGUIN			=>	'on',
	DUPLEX			=>	'off',
	SPEED			=>	'30',
	PAPERSAVE		=>	'off',
	FONT			=>	'Latex',
	PAPER			=>	'A4',
	DEBUG			=>	'0',
	REM_SIG			=>	'off',
	REM_QUOTE		=>	'off',
	WAIT			=>	'30',
	TOPMARGIN		=>	'19',
	BOTTOMMARGIN	=>	'22',
	LEFTMARGIN		=>	'20',
	RIGHTMARGIN		=>	'20',
	HEADRULE		=>	'off',
	FOOTRULE		=>	'off',
	FRONTSTYLE		=>	'border',
	MAXADDR		    =>	'10',
	DATE			=>	'original',
	DATE_FORMAT		=>	'%c',
	XFACE			=>	'off',
	PRINTED_HEADERS	=>	'Date_To_From_CC_Newsgroups_*Subject*',
	FONTSIZE		=>	'10pt',
	WRAPMARGIN		=>	80,
	VERBATIMNORMAL	=>	'',
	VERBATIMSIG		=>	'fontshape=it',
	RCFILE			=>	'',
	ADDRESSFORMAT	=>	'%r <%a>,\n',
	LATEXCODE		=>	'',
	LATEXCODE1		=>	'',
	SIG_REGEXP		=>	'^-- $',
	LATEXCODE2		=>	'',
	LATEXCODE3		=>	'',
	LATEXCODE4		=>	'',
	MAINT_SEARCH    =>  'off',
	LATEXCODE5		=>	'',
	BACKGROUND		=>	0,
	ICONV_EXTERNAL	=> 'off',
);
my %fontpackages = (
	'Latex'    		=>		'',
	'Latex-bright'	=>		'cmbright',
	'Latin-Modern'  =>      'lmodern',
	'Times'			=>		'mathptmx,courier',
	'Charter'		=>		'charter',
	'Utopia'		=>		'utopia',
	'Palatino'		=>		'mathpple,courier',
	'Bookman'		=>		'bookman',
	'CentSchool'	=>		'newcent',
	'Chancery'		=>		'chancery,courier',
	'Helvetica'		=>		'helvet-SANSSERIF-,courier',
	'AvantGarde'	=>		'avant-SANSSERIF-,courier',
);
my %EuroSign = (
	'Latex'			=>		'\EURtm{}',
	'Latex-bright'	=>		'\EURhv{}',
	'Times'			=>		'\EURtm{}',
	'Charter'		=>		'\EURtm{}',
	'Utopia'		=>		'\EURtm{}',
	'Palatino'		=>		'\EURtm{}',
	'Bookman'		=>		'\EURtm{}',
	'CentSchool'	=>		'\EURtm{}',
	'Chancery'		=>		'\EURtm{}',
	'Helvetica'		=>		'\EURhv{}',
	'AvantGarde'	=>		'\EURhv{}',
);
my @print;									# ob Hilfe / Version angezeigt werden soll
my %Temp;									# temporaere Dateien und Verzeichn.
my %Header;									# Header ...
my @PrintedMailheaders;						# Mailheader so wie er gedruckt wird (LaTeX-Code)
my $childPid;								# PID des Kinds
my $DVIopts;								# Optionen fuer DVI - Aufruf
my @Command;								# Kommando zum Drucken; fuer Druck in Datei
my $Laenge = "0";							# zum Ermitteln der laengsten Zeile
my $MaxLaenge = "0";						# die laengste Zeile
my $QP = 0;									# quoted printable encoded body
my @LastHeader = ("", 0);					# letzter gefundener Header
											# (Header, Zeilen#)
my %HeaderFormatAttr;						# Formatauszeichnungen in LaTeX-Code
my @headers;
my $image_height = "20mm";					# Hoehe des gedruckten Bildes
my $startworkingdir = getcwd();				# Arbeitsverzeichnis, wenn Muttprint
											# gestartet wird
my $signature;								# Signatur (Inhalt)
my $runningInBackground = FALSE;			# does Muttprint run *currently* in Background
my $inputSource = "-";						# - stands for stdin


############# END VARIABLES ##################################################


#
# Konfiguration einlesen

$Config{PRINTER} = $ENV{PRINTER} if defined $ENV{PRINTER};

#
# CDE default printer
{
	my $CDEprinter = getDefaultPrinterCDE();
	if ($CDEprinter) {
		$Config{PRINTER} = $CDEprinter;
	}
}

$Config{PAPER} = getPaperConfig ();
readConfig ("/etc/Muttprintrc", "$ENV{HOME}/.muttprintrc");


@print = readOpts ();

if ($Config{PENGUIN} eq "on") {
	my $sharedir = findCommonDir("share");
	$Config{PENGUIN} = (-r "$sharedir/penguin.eps")
			? "$sharedir/penguin.eps" 
			: "off";
}

readConfig ($Config{RCFILE});



################ MULIT LANGUAGE SUPPORT #######################################

convert_init();
POSIX::setlocale(&POSIX::LC_ALL, "");
setStrings();

#
# Show help and version
#
# 0 => help (return 0)
# 1 => version
# 2 => print locale
# 3 => error -> help (return 1)

if ($print[0]) {
	outputstd "\n".$String{Usage}."\n\n".$String{Bugs}."\n\n";
	exit 0;
} elsif ($print[1]) {
	outputstd "\n"."Muttprint $VERSION"."\n\n".$String{License}."\n\n";
	exit 0;
} elsif ($print[2]) {
	outputstd "Encoding ......: ".getLocaleInformation(CHARSET)."\n";
	outputstd "Language ......: ".getLocaleInformation(LANGUAGECODE)."\n";
	outputstd "Country .......: ".getLocaleInformation(COUNTRYCODE)."\n";
	outputstd "Babel option ..: ".getLocaleInformation(BABEL_LANGUAGE)."\n";
	outputstd "TeX inputenc ..: ".getLocaleInformation(CHARSET_TEX_NOTATION)."\n";
	outputstd "TeX fontenc ...: ".getLocaleInformation(TEX_FONTENC)."\n";
	exit 0;
} elsif ($print[3]) {
	fatalError "You used an option which does not exist. Maybe this is because ".
		"there have been options removed since the last version. Read the manual ".
		"and the manpages to find out more. Read also the CHANGES file to see ".
		"what has been changed. If this file is not installed, then try ".
		"http://muttprint.sf.net/changes.shtml\n\n".
		"--> For quick information try \"muttprint --help\" <--";;
	exit 1;
}

if ($Config{MAINT_SEARCH} eq 'on') {
	
	open RCFILE, ">>$ENV{HOME}/.muttprintrc" 
		or fatalError( "Could not open \$HOME/.muttprintrc for writing\n");
    print RCFILE "\nMAINT_SEARCH=\"off\"\n";
	close RCFILE;

    printWarning( "Dear Muttprint user,\n\n"
		       ."Muttprint is looking for a new maintainer. "
			   ."If you want to takeover Muttprint developement, "
			   ."contact\n\n"
			   ."      lukas.ruf\@lpr.ch\n\n"
			   ."Thanks!\n\n"
			   ."This message is disabled automatically.\n"
		   );
	
}

# default settings 
# we need to set this here because we want that configuration file
# options overwrite language file options but we need to read the
# configuration file first because in this file the language can
# be set (some sort of hen-egg-problem)


# USE FOR DEBUG ONLY
if ($Config{DEBUG}) {
	my @configs;
	foreach (keys %Config) {
		if (defined $Config{$_}) {
			push (@configs, sprintf("%-20.20s  => %-40.40s\n", $_, $Config{$_}));
		}
	}
	@configs = sort(@configs);
	print "=" x 70, "\n";
	foreach (@configs) { print; }
	print "=" x 70, "\n";
}


#
# Some printer settings. We need this here because of the `no real mail' case
#

# Using cups
if ($Config{PRINT_COMMAND} eq "CUPS") {
	$Config{PRINT_COMMAND} = 'lpr $CUPS_OPTIONS';
}

#
# are we using cups?
my $useCups = ( ($Config{PRINT_COMMAND} =~ /\$CUPS_OPTIONS/) && 
		($Config{PRINTER} !~ /^TO_FILE/) )
	? 1
	: 0;

# Need to substitute in the printer name if the printer was
# set. If not we use 'lp' as a fallback printer name
if ($Config{PRINTER}) {
	if ($useCups) {
		$Config{PRINT_COMMAND} =~ s/\$CUPS_OPTIONS/ -P $Config{PRINTER} \$CUPS_OPTIONS/;
	}
	# no else case because $CUPS_OPTIONS and $PRINTER could be both in PRINT_COMMAND
	$Config{PRINT_COMMAND} =~ s/\$PRINTER/$Config{PRINTER}/g;
} else {
	$Config{PRINT_COMMAND} =~ s/\$PRINTER/lp/g;
}

#
# Formatierung fuer Header
foreach (split (/_/, $Config{'PRINTED_HEADERS'})) {
	my $unformated = $_;
	my $formats = "";
	$unformated =~ s/[\*\/]//g;
	
	$formats .= '\\bfseries ' if /\*.*\*/;
	$formats .= '\\itshape ' if /\/.*\//;
	
	$HeaderFormatAttr{$unformated} = $formats;
	push @headers, $unformated;
}

{
	my @KompletteMail;
	
	if ($inputSource eq "-") {
		@KompletteMail = input \*STDIN;
	} else {
		open MAIL, $inputSource or fatalError "Could not read $inputSource:\n$!";
		@KompletteMail = input \*MAIL;
		close MAIL or fatalError "Could not read $inputSource:\n$!";
	}

	my $Content;

	my $signature_mode;
	my $sig_mod_counter = 0;

	for (my $i = 0; $_ = $KompletteMail[$i]; $i++) {
	
		$. = $i;

		# for software that outputs \r\n-line-Endings
		s/[\r\f]+//g;
	
		#
		# signature 
		if (((/$Config{SIG_REGEXP}/o && !($Config{REM_SIG} eq "on")) ||
			$signature_mode) && $Config{VERBATIMSIG} ne "raw") {

			if (/$Config{SIG_REGEXP}/o) {
				$signature_mode = TRUE;

				# Leerzeile bei 2. Signatur
				if (defined $signature) {
					$signature .= "\n";
				}
			} 
			else {
				$signature .= $_;
			}

			# 2 Leerzeilen => Ende der Signatur
			if (/^$/ and $sig_mod_counter == 0) {
				$sig_mod_counter++;
			} elsif (/^$/ and $sig_mod_counter == 1) {
				$signature_mode = FALSE;
			} else {
				$sig_mod_counter = 0;
			}

			next;
		}
	
		#
		# and what's about Quoting?
		next if (($Config{'REM_QUOTE'} eq "on") && (/^([\t]*[|>:}#])+/));
	
	
		#
		# Do sth with the header
		if (0 ... /^$/) {
	
			my $aktHeader;
		
			##$QP = 1 if /^Content-Transfer-Encoding:[\t ]+quoted-printable/i;
			##($mail_charset) = /charset=(["'A-Za-z0-9\-_]*)/i unless $mail_charset; 
			
			foreach $aktHeader (@headers, "X-Face") {
			
				if (/^${aktHeader}:[\t ]+/i) {
					@LastHeader = ($aktHeader, $.);
					if (exists $Header{$aktHeader}) {
						$Header{$aktHeader} .= "\n ";
					}
					chomp($Header{$aktHeader} .= $');
				} 
				elsif (/^[\t ]+/i && ($LastHeader[1] + 1 == $.)) {
					chomp($Header{"$LastHeader[0]"} .= " $'");
					$LastHeader[1] ++;
				}
			}
		}
	
	
		#
		# ... and do sth with the body
		else {
	
			$Content .= $_;
				
			$Laenge = length($_);
			$MaxLaenge = $Laenge if ($Laenge > $MaxLaenge);
		
		}
	}

	# ab in den Hintergrund 

	if (!$Config{DEBUG} && $Config{BACKGROUND}) {
	  fork && exit;
	  $runningInBackground = TRUE;
	}

	#
	# wenn die Header nicht ausgelesen werden koennen: so drucken!		
	unless ($Header{From} || $Header{To} || $Header{Subject}) { 
		$Config{PRINT_COMMAND} =~ s/\$CUPS_OPTIONS//g;
        
		open (PRINTER, "| $Config{PRINT_COMMAND}") 
			or fatalError "Unable to print with $Config{PRINT_COMMAND}:\n$!";
		output \*PRINTER, @KompletteMail;
		close PRINTER;
		exit;
	
	}


	# formatieren
	
	# Body dekodieren
	if ($QP) {
		$Content =~ s/=([A-Fa-f0-9]{2})/chr(hex($1))/eg;
	}

	createTemp();
	open (CONTENT, "> $Temp{'content'}") 
		or fatalError "Unable to create $Temp{'content'}:\n$!";

	if ($MaxLaenge > $Config{WRAPMARGIN}) {
		writeFormated($Content, \*CONTENT, $Config{WRAPMARGIN});
	
	} else {
		output \*CONTENT, $Content;
	}

	close CONTENT;

}


# Realname
$Header{'ShortFrom'} = getShortFrom ($Header{'From'}, $Header{'To'});

#
# passendes Eurozeichen
my $EuroSign = '\EURtm{}';
if (exists $Config{FONT} and exists $EuroSign{$Config{FONT}}) {
	$EuroSign = $EuroSign{$Config{FONT}};
}


my $type;
my %Count;
foreach $type (@headers) {
	
	if (defined $Header{$type}) {
		
		if ($type =~ /^Date/i) {
			$Header{$type} =~ s/-/--/;

			# Datum konvertieren
			if ($Config{DATE} eq "local") {
				$Header{Date} = convertDate($Header{Date});
			}
		}
		else {
			for ($Header{$type}) {
				
				my $count = 0;
				#
				# Header formatieren nach ADDRESSFORMAT
				if ($type =~ /^(To|From|CC)/i && $Config{ADDRESSFORMAT} ne "original") {
					# Adressen 'einrahmen'
					s/(?:^|\s)([^<>'"\s]+\@[^<>\s,'"]+)/<$1>/g;
					s/<+/</g;
					s/>+/>/g;

					my $spezheader;
					my $newheader;
					foreach $spezheader (split /(?<=>),/, $_) {
						my $realname = getRealname($spezheader) || "";
						my $format = $Config{ADDRESSFORMAT} || "%r <%a>\n";
						my $address = ($spezheader =~ /(?<=<)(\S+\@\S+)(?=>)/)[0] || "";

						if ($count < $Config{MAXADDR}) {
							if ($realname !~ /^\s*$/) {
								$format =~ s/\*(.+)?\*/\0\\textbf\0{$1\0}/g;
								$format =~ s/\/(.+)?\//\0\\textit\0{$1\0}/g;
								$format =~ s/%r/$realname/g;
								$format =~ s/%a/$address/g;
								$format =~ s/\\n/\n/g;
								$newheader .= $format;
							}
							else {
								$newheader .= $format =~ /\n$/ ? "$address,\n" : "$address,";
							}
							$newheader .= " ";
						}
						else {
							if ($count == $Config{MAXADDR})  {
								$newheader .= " \n \0\\ldots ";
								$count++;
							}
						}
						
						$count ++;
						
					}

					$newheader =~ s/[\s,\n]+$//g;
					$_ = $newheader;
				}
					
				s/(?<!\0)\{/\\\{/g;
				s/(?<!\0)\}/\\\}/g;

				if ($type =~ /from|to|cc/i) {
					s/(?<!\0)\\\)/\)/g;
					s/(?<!\0)\\\(/\(/g;
				}

				s/(?<!\0)\\/\\textbackslash\{\}/g;
				s/\n/\\newline/g;
				s/\"/\\textquotedbl\{\}/g;
				s/>/\\textgreater\{\}/g;
				s/</\\textless\{\}/g;
				s/\#/\\\#/g;
				s/\&/\\&/g;
				s/\$/\\\$/g;
				s/\|/\\\|/g;
				s/\~/\\\~{}/g;
				s/\^/\\\^{}/g;
				s/\%/\\\%/g;
				s/_/\\_/g;
				s/-/{-}/g;
				s/\0//g;
			}
		}

		my $ftype = "\L\u$type";
		$ftype = "Message-ID" if ($ftype eq "Message-id");
		
		unless (defined $String{$ftype}) {
			$String{$ftype} = "$ftype:";
		}
		
		push @PrintedMailheaders,
			"{ $HeaderFormatAttr{$type} $String{$ftype}} \& ".
			"$HeaderFormatAttr{$type} $Header{$type}  \& \\\\ \n";
				
	}
}


# # XFACE-Support

if ($Config{'XFACE'} eq "on" && exists $Header{'X-Face'}) {
	changeForXface($Header{'X-Face'});
}

#
# Papierformat
my $paperformat = ($Config{PAPER} eq "letter")
	? "letter"
	: "a4";

#
# redirection of the error output
my $errorRedirection = $Config{DEBUG}
	? "$Temp{logf}" 
	: "/dev/null";


#
# LaTeX-Datei erzeugen
createLatex();

chdir($Temp{dir});

#
# running latex twice because of the "page ... of ..."
system("latex -interaction=nonstopmode mail.tex >> $errorRedirection 2>&1");
system("latex -interaction=nonstopmode mail.tex >> $errorRedirection 2>&1");

#
# no check of the exit code because we do this here
unless (-e $Temp{dvi}) {
	fatalError "Latex didn't work. There's no DVI file. If you ".
		"write a bugreport, please include a mail where printing fails.";
}


##################################### PRINTING ###############################


#
# generating the postscript file in every case
system("dvips -t $paperformat -o $Temp{ps} $Temp{dvi} >> $errorRedirection 2>&1")
	and fatalError "Error while running dvips:\n$!";


# now we find out the number of pages the document has
my $numberOfPages = getNumberOfPages("$Temp{dir}/mail.aux");

#
# if the papersave mode is `optional' we need to determine whether
# it makes sense to turn papersave on, i.e. if there's more than
# one page to print
if ($Config{PAPERSAVE} eq "optional") {
	$Config{PAPERSAVE} = ($numberOfPages > 1)
		? "on"
		: "off";
}


#
# setting the paperformat for Cups
if ($useCups) {
	$Config{PRINT_COMMAND} =~ s/\$CUPS_OPTIONS/ -o media=\U$paperformat\E \$CUPS_OPTIONS/;
}

#
# papersave mode
if ($Config{PAPERSAVE} eq "on") {
	if ($useCups) {
		$Config{PRINT_COMMAND} =~ s/\$CUPS_OPTIONS/ -o number-up=2 \$CUPS_OPTIONS/;
	} else {
		system("psnup -2 -p$paperformat -P$paperformat -q $Temp{ps} ".
			"$Temp{psnew} >> $errorRedirection 2>&1") 
			and fatalError "Psnup failed:\n$!";
		unlink($Temp{ps});
		link($Temp{psnew}, $Temp{ps});
	}
}

#
# duplex printing with a `real' duplex printer
if ($Config{DUPLEX} eq "printer") {
	if ($Config{PAPERSAVE} eq "on") {
		if ($useCups) {
			$Config{PRINT_COMMAND} =~ 
				s/\$CUPS_OPTIONS/ -o sides=two-sided-long-edge \$CUPS_OPTIONS/;
		} else { # no cups
			modifyPS("landscape");
		}
	} else { # no papersave
		if ($useCups) {
			$Config{PRINT_COMMAND} =~ 
				s/\$CUPS_OPTIONS/ -o sides=two-sided-short-edge \$CUPS_OPTIONS/;
		} else { # no cups
			modifyPS("portrait");
		}
	}
}

#
# simulated duplex printing
if ($Config{DUPLEX} eq "on") {
	if ($useCups) {
		$Config{PRINT_COMMAND} =~ 
				s/\$CUPS_OPTIONS/ -o page-set=oddeven \$CUPS_OPTIONS/;
	} else {
		system("psselect -q -o $Temp{ps} $Temp{ps1} >> $errorRedirection 2>&1")
			and die "Psselect failed: $!";
		system("psselect -q -e $Temp{ps} $Temp{ps2} >> $errorRedirection 2>&1")
			and die "Psselect failed: $!";
	}
}
			

#
# printing in a file
if ($Config{'PRINTER'} =~ /^(TO_FILE|file):[^-]+/i) {
	my $file1 = "$ENV{HOME}/muttprint.ps";
	my $file2 = "$ENV{HOME}/muttprint2.ps";

	$file1 = $Config{PRINTER};
	$file1 =~ s/(TO_FILE|file)://;

	# if not an absoute path
	if ($file1 !~ /\//) {
		$file1 = "$startworkingdir/$file1";
	}

	# do we have two printfiles?
	if ($Config{DUPLEX} eq "on") {
		$file2 = $file1;
		$file2 =~ s/\.(\w+)$/2.$1/;

		# sure is sure
		if ($file1 eq $file2) {
			$file2 .= "2";
		}
	}

	if ($Config{DUPLEX} eq "on") {
		copyFile($Temp{ps1}, $file1);
		copyFile($Temp{ps2}, $file2);
	} else {
		copyFile($Temp{ps}, $file1);
	}
	
} elsif ($Config{'PRINTER'} =~ /^((TO_FILE|file):-?|-)$/i) {
	
	# Just write the postscript code to STDOUT
	open (PS, "<$Temp{ps}") or die "Could not open $Temp{ps} for reading: $!";
	while (<PS>) {
		outputstd $_;
	}
	close PS or fatalError "Could not close $Temp{ps}:\n$!";

} else { # here's the code for normal printing
	# remove CUPS_OPTIONS here
	$Config{PRINT_COMMAND} =~ s/\$CUPS_OPTIONS//;

	# correct the number of pages to the `real' number
	if ($Config{PAPERSAVE} eq "on") {
		$numberOfPages = POSIX::ceil($numberOfPages/2);
	}
	
	if ( ($Config{DUPLEX} ne "on") || ($numberOfPages == 1) ) {
		# we use the print command as pipe because it's more flexible
		system("cat $Temp{ps} | $Config{PRINT_COMMAND} >> $errorRedirection 2>&1")
			and fatalError "Could not print with $Config{PRINT_COMMAND}:\n $!";
	} else { # duplex is on
		my $sleepTime = 0;

		# calculating the sleep time
		$sleepTime = $Config{SPEED} * $numberOfPages + $Config{WAIT};

		if ($useCups) {
			printDuplexCups($Config{PRINT_COMMAND}, $Temp{ps}, $sleepTime);
		} else {
			printDuplexNoCups($Config{PRINT_COMMAND}, $Temp{ps1}, $Temp{ps2}, $sleepTime);
		}
	}
}

################################### ENDE ####################################


##################### UNTERFUNKTIONEN ########################################

sub printDuplexNoCups ($$$$) {

	my $printCommand = shift;
	my $firstFile = shift;
	my $secondFile = shift;
	my $timeBetween = shift;

	if (!defined($childPid = fork())) {
		fatalError "Could not fork:\n$!";
	} elsif ($childPid) {
		system ("cat $firstFile | $printCommand >>$errorRedirection 2>&1")
			and fatalError "Error while running lpr:\n$!";
	} else {
		sleep ($timeBetween);
		system ("cat $secondFile | $printCommand >>$errorRedirection 2>&1")
			and fatalError "Error while running lpr:\n$!";
		exit;
	}
}

##############################################################################

sub printDuplexCups ($$$) {

	my $printCommand = shift;
	my $file = shift;
	my $timeBetween = shift;

	if (!defined($childPid = fork())) {
		fatalError "Couldn't fork:\n$!";
	} elsif ($childPid) {
		$printCommand =~ s/oddeven/odd/;
		system ("cat $file | $printCommand >> $errorRedirection 2>&1")
			and fatalError "Error while running lpr: $!";
	} else {
		$printCommand =~ s/oddeven/even/;
		sleep ($timeBetween);
		system ("cat $file | $printCommand >> $errorRedirection 2>&1")
			and fatalError "Error while running lpr: $!";
		exit;
	}
}

##############################################################################


#
# Liest die Konfigurationsdatei ein

sub readConfig (@) {

	my @rcfiles = @_;
	my $rcfile;
	
	foreach $rcfile (@rcfiles) {

		next unless (-r $rcfile);
	
		open (RCFILE, $rcfile) or fatalError "Could not open $rcfile:\n$!";

		while (<RCFILE>) {
			if (/^([^#=\s]+)=(["']?)(.*)\2\s+$/) { 
				$Config{$1} = $3; 
			}
		}
			
		close RCFILE or fatalError "Could not close $rcfile:\n$!";
		
	}
}

##############################################################################

sub readOpts () {

	# wird fuer die Optionen benoetigt (gehoert zum Standardumfang von Perl 5)
	use Getopt::Long; 
	Getopt::Long::Configure ("no_ignore_case");

	
	my %opt;									# Aufrufoptionen	
	my $error = 0;								# Beenden mit Fehler
	
	#
	# Optionen einlesen und der zugehoerigen Variablen zuordnen
	
	GetOptions (
	
		'h|help' 				=> 	\$opt{help},
		'v|version'				=>	\$opt{version},
		'print-locale'			=>	\$opt{print_locale},

		'p|printer=s' 			=>	\$Config{PRINTER},
		'C|printcommand=s'		=>	\$Config{PRINT_COMMAND},
		'i|penguin=s'			=>	\$Config{PENGUIN},
		't|speed=i'				=>	\$Config{SPEED},
		'w|wait=i'				=>	\$Config{WAIT},
		'F|font=s'				=>	\$Config{FONT},
		'P|paper=s'				=>	\$Config{PAPER},
		'S|frontstyle=s'		=>	\$Config{FRONTSTYLE},
		'a|printed-headers=s'	=>	\$Config{PRINTED_HEADERS},
        'z|fontsize=s'          =>  \$Config{FONTSIZE},
        'W|wrapmargin=s'        =>  \$Config{WRAPMARGIN},
		'D|debug!'				=>	\$Config{DEBUG},
		'B|background!'			=>	\$Config{BACKGROUND},
		'e|date=s'				=>	\$Config{DATE},
		'E|date-format=s'		=>	\$Config{DATE_FORMAT},
		'r|rcfile=s'			=>	\$Config{RCFILE},
		'A|addressformat=s'		=>	\$Config{ADDRESSFORMAT},
		'g|topmargin=s'			=>	\$Config{TOPMARGIN},
		'G|bottommargin=s'		=>	\$Config{BOTTOMMARGIN},
		'j|leftmargin=s'		=>	\$Config{LEFTMARGIN},
		'J|rightmargin=s'		=>	\$Config{RIGHTMARGIN},
		'n|verbatimnormal=s'	=>	\$Config{VERBATIMNORMAL},
		'V|verbatimsig=s'		=>	\$Config{VERBATIMSIG},
		'sig_regexp=s'			=>	\$Config{SIG_REGEXP},


		'd|duplex!'				=>	\$opt{DUPLEX},
		'x|x-face!'				=>	\$opt{XFACE},
		'H|headrule!'			=>	\$Config{HEADRULE},
		'b|footrule!'			=>	\$Config{FOOTRULE},
		'1'						=> 	\$opt{paper1},
		'2'						=>	\$opt{paper2},
		's|rem_sig!'			=>	\$opt{REM_SIG},
		'q|rem_quote!'			=>	\$opt{REM_QUOTE},
		
		'f|file=s'				=>	\$opt{file},
		
	) or $error = 1;
	
	
	#
	# Logische Optionen => on/off
	
	foreach (qw /DUPLEX REM_SIG REM_QUOTE HEADRULE FOOTFULE XFACE/) {
		next unless defined $opt{$_};
		
		if ($opt{$_}) {
			$Config{$_} = "on";
		}
		else {
			$Config{$_} = "off";
		}	
	}
	
	#
	# Papiersparmodus
	
	$Config{'PAPERSAVE'} 	= "off"			if $opt{'paper1'};
	$Config{'PAPERSAVE'}	= "on"			if $opt{'paper2'};

	#
	# andere Sprache
	$Config{'LANG'} 		= $opt{'LANG'}	if defined $opt{'LANG'};

	# aus Datei lesen

	if (defined $opt{'file'}) {
		unless ($opt{'file'} eq "-") {
			unless (-e $opt{'file'}) {		
				fatalError "Could not open $opt{'file'}. Maybe the file does not exist.\n";
			}
			$inputSource = $opt{'file'};
		}			
	}
	
	# Rueckgabe: Wahrheitswerte fuer 
	# I Hilfe, II Version, III Locale

	return ($opt{help}, $opt{version}, $opt{print_locale}, $error);

}

##############################################################################

sub getPaperConfig () {

	my $DebianPaper = "A4";
	my $paperFormat;
	my $Papersize = "/etc/papersize";

	return unless (-r $Papersize);

	open (RCFILE, $Papersize) or fatalError "Could not open $Papersize:\n$!";
	chomp($DebianPaper = <RCFILE>);
	close RCFILE or fatalError "Could not close $Papersize:\n$!";

	foreach ($DebianPaper) {
		/a4/i 		&& 	do { $DebianPaper = "A4";	 		last };
		/letter/i	&&	do { $DebianPaper = "letter";		last };
	}

	return $DebianPaper;
}

##############################################################################

{
	my $countrycode;
	my $languagecode;
	my $charset;
	
	sub getCharsetTexNotation () {
		for ($charset) {
			/iso[_\-]8859[_\-]15/i		&& return "latin9";
			/iso[_\-]8859[_\-]1/i 		&& return "latin1";
			/iso[_\-]8859[_\-]2/i 		&& return "latin2";
			/iso[_\-]8859[_\-]3/i		&& return "latin3";
			/iso[_\-]8859[_\-]4/i		&& return "latin4";
			/iso[_\-]8859[_\-]9/i		&& return "latin5";
			/(cp|windows)[_\-]?1252/i	&& return "latin1";
			/(cp|windows)[_\-]?1250/i	&& return "latin2";
			/utf[-_]8/i					&& return "utf8";
			/koi8[-_]r/i				&& return "koi8-r";
			/us[-_]ascii/i				&& return "latin1";

			return "latin1";
		}
	}

	sub getBabelLanguage () {
		my $full_locale = $countrycode."_".$languagecode;

		# all languages supported by babel according to the documentation
		my %babel = (
			af		=> "afrikaans",
			in		=> "bahasa",
			eu		=> "basque",
			br		=> "breton",
			bg		=> "bulgarian",
			ca		=> "catalan",
			hr		=> "croatian",
			cs		=> "czech",
			da		=> "danish",
			nl		=> "dutch",
			en_US	=> "american",
			en_GB	=> "british",
			en_CA	=> "canadian",
			en		=> "english",
			eo		=> "esperanto",
			et		=> "estonian",
			fi		=> "finnish",
			fr		=> "french",
			gl		=> "galician",
			de		=> "ngerman",
			de_AT	=> "naustrian",
			gr		=> "greek",
			iw		=> "hebrew",
			hu		=> "magyar",
			is		=> "icelandic",
			ga		=> "irish",
			it		=> "italian",
			la		=> "latin",
			lp		=> "samin",
			no		=> "norsk",
			po		=> "polish",
			pt		=> "portuguese",
			pt_BR	=> "brazil",
			ro		=> "romanian",
			ru		=> "russian",
			es		=> "spanish",
			sk		=> "slovak",
			sl		=> "slovene",
			sv		=> "swedish",
			sr		=> "serbian",
			tr		=> "turkish",
			uk		=> "ukrainian",
			cy		=> "welsh"
		);

		if (defined $babel{$full_locale}) {
			return $babel{$full_locale};
		} elsif (defined $babel{$languagecode}) {
			return $babel{$languagecode};
		} else {
			return "english";
		}
	}
	
	# helping functions
	sub message_locale {
		if (defined $ENV{LC_ALL}) {
			return $ENV{LC_ALL};
		} elsif (defined $ENV{LC_MESSAGES}) {
			return $ENV{LC_MESSAGES};
		} elsif (defined $ENV{LANG}) {
			return $ENV{LANG};
		} else {
			return "en_US";
		}
	}
	sub get_language {
		my $locale = message_locale();
		my $language = "en";
		if (length($locale) >= 2 && $locale =~ /^[[:lower:]]{2}/) {
			$language = substr($locale, 0, 2);
		}
		return $language;
	}

	sub get_country {
		my $locale = message_locale();
		my $country = "US";
		if (length($locale) >= 5 && $locale =~ /^[[:lower:]]{2}_[[:upper:]]{2}/) {
			$country = substr($locale, 3, 2);
		} 
		return $country;
	}

	sub get_charset {
		my $charset = `locale charmap`;
		chomp($charset);
		if ($charset eq "ANSI_X3.4-1968") {
			$charset = "US-ASCII";
		}
		return $charset;
	}


	sub setLocaleInformation {
		
		$languagecode = get_language();
		$countrycode = get_country();
		
		# try to use the perl module
		eval {
			require I18N::Langinfo;
			I18N::Langinfo->import(qw(langinfo CODESET));
			$charset = langinfo(CODESET()); # note the ()
		};
		
		if ($@) { 
			no warnings;
			my $output = `muttprint-langinfo -c`;
			if (defined $output) {
				chomp $output;
				$charset = $output;
			} else {
				if ($^O =~ /linux/i) {
					$output = `locale charmap`;
					if (!defined $output) {
						fatalError "There's no \"locale\" in \$PATH. Since you run Linux this is ".
							"confusing. Please install this small \"locale\" program, update to ".
							"Perl 5.8.1 or install muttprint-langinfo which is distributed ".
							"with Muttprint.";
					}
					chomp $output;
					$charset = $output;
				} else {
					fatalError "There's no muttprint-langinfo in \$PATH. Please install it ".
						"or update to Perl 5.8.1. The C program should be work on all systems which ".
						"follow the \"The Single UNIX(R) Specification, Version 2\". You find ".
						"it in the langinfo/ subdirectory of the Muttprint source distribution.";
				}
			}
		}
	}

	sub getLocaleInformation ($) {
		my $type = shift;
		
		unless (defined $countrycode) {
			setLocaleInformation();
		}

		if ($type == LANGUAGECODE) {
			return $languagecode;
		} elsif ($type == COUNTRYCODE) {
			return $countrycode;
		} elsif ($type == CHARSET) {
			return $charset;
		} elsif ($type == CHARSET_TEX_NOTATION) {
			return getCharsetTexNotation();
		} elsif ($type == BABEL_LANGUAGE) {
			return getBabelLanguage();
		} elsif ($type == TEX_FONTENC) {
			if (defined $Config{TEX_FONTENC}) {
				return $Config{TEX_FONTENC};
			} else {
				return $texFontenc ? $texFontenc : "T1";
			}
		}
	}
}

sub createTemp () {
	#
	# temp directory / temp files
	$Temp{dir} = tempdir("muttprint-XXXXXX", TMPDIR => 1, CLEANUP => 1);
	$Temp{content} = "$Temp{dir}/content";
	$Temp{latex} = "$Temp{dir}/mail.tex";
	$Temp{logf} = "/tmp/muttprint.log";
	$Temp{dvi} = "$Temp{dir}/mail.dvi";
	$Temp{ps} = "$Temp{dir}/mail.ps";
	$Temp{psnew} = "$Temp{dir}/mail-new.ps";
	$Temp{ps1} = "$Temp{dir}/mail1.ps";
	$Temp{ps2} = "$Temp{dir}/mail2.ps";
	$Temp{xf_raw} = "$Temp{dir}/xface.raw";
	$Temp{xf_xbm} = "$Temp{dir}/xface.xbm";
	$Temp{xf_eps} = "$Temp{dir}/xface.eps";
}

##############################################################################

sub findCommonDir ($) {

	my $sort = shift;
	my $prefix = "";
	
	$prefix = $0 =~ m#(.*)/bin/muttprint#;
	
	foreach ($prefix, "/usr/", "/usr/local", $ENV{HOME}) {
		my $common_dir = "$_/$sort/muttprint/";
		if (-d $common_dir) {
			return $common_dir;
		}
	}
}

##############################################################################

sub changeForXface ($) {

	open (RAW, ">$Temp{'xf_raw'}") or fatalError "Could not create XF-Raw file:\n$!";
	binmode RAW;
	print RAW @_;
	close RAW;

	system ("uncompface -X $Temp{xf_raw} $Temp{xf_xbm}") 
		and fatalError "Could not convert XF-Raw into XF-XBM:\n".
			"Maybe 'uncompface' not installed:\n$!";
	
	system ("convert $Temp{xf_xbm} $Temp{xf_eps}")
		and fatalError "Could not convert XF-XBM into XF-EPS:\n".
			"Maybe 'convert' not installed: $!";
	
	$Config{'PENGUIN'} = $Temp{xf_eps};

	$image_height = "15mm";

}

##############################################################################

sub createLatex () {

	my $PengCode = "";						# Kommando fuer den Pinguin
	my $PengTab;			 				# restl. Groesse fuer Pinguin
	my $BeforeHeader;
	my $sansserif = '';
	my $AfterHeader;
	my $fontpackage = '';
	$fontpackage = $fontpackages{$Config{'FONT'}} if exists $fontpackages{$Config{'FONT'}};
	my $headerrule_other = '\\renewcommand{\\headrulewidth}{0pt}';
	my $headerrule_first = '\\renewcommand{\\headrulewidth}{0pt}';
	my $footerrule = "";
	my $fontencString;
	my $babellanguage = getLocaleInformation(BABEL_LANGUAGE);
	my $inputcharset = getLocaleInformation(CHARSET_TEX_NOTATION);
	my $isolatinextensions = getISOlatinExtensions($inputcharset);


	# Sans-Serif-Verhalten
	if ($fontpackage =~ /-SANSSERIF-/) {
		$fontpackage =~ s/-SANSSERIF-//;
		$sansserif = '\renewcommand{\familydefault}{\sfdefault}';
	}

	my $LatexPackages = "$fontpackage,fancyhdr,lastpage";

	# Richtiges Tab-Verhalten
	foreach (qw/VERBATIMNORMAL VERBATIMSIG/) {
		$Config{$_} = 'obeytabs=true,' . $Config{$_};
	}

	# LaTeX Fontencoding
	my $tex_fontec = getLocaleInformation(TEX_FONTENC);
	$fontencString = defined $tex_fontec 
		? "\\usepackage[$tex_fontec]{fontenc}"
		: "";

	# Pinguin drucken?

	if ($Config{'PENGUIN'} ne "off") {
		$PengCode = <<"EOF";
\\raisebox{4mm}{
\\begin{minipage}[t]{20mm}
\\begin{flushright}
~ \\\\
\\includegraphics[height=$image_height]{$Config{'PENGUIN'}}
\\end{flushright}
\\end{minipage}}
EOF
		$PengTab = 155 - $Config{LEFTMARGIN} - $Config{RIGHTMARGIN};
	}
	else {
		$PengTab = 170 - $Config{LEFTMARGIN} - $Config{RIGHTMARGIN};
	}

	my $sig_tex = "";
	
	if (defined $signature) {
		$sig_tex = <<"EOF";
\\begin{Verbatim}[$Config{VERBATIMSIG}]
$signature
\\end{Verbatim}
EOF
	}

	if ($Config{PAPER} eq "letter") {
		$PengTab += 6;			# Letter ist breiter
	}

	$PengTab .= "mm";			# Masseinheit anhaengen

	#
	# Frontstyle:
	for ($Config{'FRONTSTYLE'}) {
		/^plain$/i 			&& do {		
			$BeforeHeader = "";
			$AfterHeader = "\\vspace{8mm}";				
		last; };
			
		/^fbox$/i 			&& do {
			$BeforeHeader = '\\fbox{';
			$AfterHeader = '} \\vspace{5mm}';
		last; };
		/^shadowbox$/i		&& do {
			$LatexPackages .= ",fancybox";
			$BeforeHeader = '\\shadowbox{';
			$AfterHeader = '} \\vspace{3mm}';
		last; };
		/^(?-i:o)valbox$/i	&& do {
			$LatexPackages .= ",fancybox";
			$BeforeHeader = '\\ovalbox{';
			$AfterHeader = '} \\vspace{6mm}';
		last; };
		/^(?-i:O)valbox$/i	&& do {
			$LatexPackages .= ",fancybox";
			$BeforeHeader = '\\Ovalbox{';
			$AfterHeader = '} \\vspace{6mm}';
		last; };
		/^doublebox$/i		&& do {
			$LatexPackages .= ",fancybox";
			$BeforeHeader = '\\doublebox{';
			$AfterHeader = '} \\vspace{5mm}';
		last; };
		/^grey$/i			&& do {
			$LatexPackages .= ",color";
			$BeforeHeader = '\\colorbox[gray]{0.85}{';
			$AfterHeader = '} \\vspace{8mm}';
		last; };
		/^greybox$/i		&& do {
			$LatexPackages .= ",color";
			$BeforeHeader = '\\definecolor{light}{gray}{0.85} \\fcolorbox{black}{light}{';
			$AfterHeader = '} \\vspace{6mm}';
        last; };
        /^(?-i:B)order$/i	&& do {
        $BeforeHeader = "";
        $AfterHeader = '\\vspace{5mm} \\hrule height 1truemm \\vspace{5mm}';
		last; };
	

			$BeforeHeader = "";
			$AfterHeader = '\\vspace{5mm} \\hrule \\vspace{5mm}';	
	}
	
	#
	# Headerrule
	if ($Config{'HEADRULE'} eq "on") {
		$headerrule_other = '\\renewcommand{\\headrulewidth}{0.5pt}';
	}
	
	#
	# Footerrule
	if ($Config{'FOOTRULE'} eq "on") {
		$footerrule = '\\renewcommand{\\footrulewidth}{0.5pt}';
	}
	
	
	# if there's no subject:
	$Header{'Subject'} ||= "(no subject)";

	# short Subject for head line
	$Header{'ShortSubject'} = $Header{'Subject'};
	my $numberReplacements = 0;
	
	# it's better to limit the number of substitutions to 10 because there
	# might be an error in my regexp and endless loops aren't very good :-)
	while (length($Header{ShortSubject}) > 60 && $numberReplacements < 10) {
		$Header{'ShortSubject'} =~ s/\s+\S+(\s+\\ldots)*(\s+\S+)$/ \\ldots$2/;
		$numberReplacements++;
	}

	# page of ...
	my $pageString;
	unless (exists $String{PageOf}) {
		$String{PageOf} = "$String{Page} %s $String{of} %s";
	}
	$pageString = sprintf $String{PageOf}, "\\thepage{}", "\\pageref{LastPage}";

	open (LATEX, "> $Temp{'latex'}")
		or fatalError "Unable to create $Temp{'latex'}:\n$!";


#
# hier wird die eigentliche LaTeX-Quelldatei erzeugt

output \*LATEX, "
\\documentclass[compat2,$Config{FONTSIZE}]{article}
$fontencString
\\renewcommand{\\tt}{\\rm}
\\usepackage[$babellanguage]{babel}
\\usepackage[$inputcharset]{inputenc}
\\usepackage{$LatexPackages}
\\usepackage[${paperformat}paper,left=$Config{LEFTMARGIN}mm,right=$Config{RIGHTMARGIN}mm,%
	top=$Config{TOPMARGIN}mm,bottom=$Config{BOTTOMMARGIN}mm,headsep=5mm]{geometry}
\\usepackage{fancyvrb,graphicx,marvosym,textcomp,array}
$sansserif
\\setlength{\\parindent}{0mm}
$isolatinextensions
\\pagestyle{fancy}
\\lhead{\\itshape ";
	
	output \*LATEX, $Header{ShortFrom};

output \*LATEX, "}
\\rhead{\\bfseries ";

	output \*LATEX, $Header{'ShortSubject'};

output \*LATEX, " } 
\\cfoot{}
\\lfoot{\\today}
\\rfoot{", $pageString, "}
$headerrule_other
$footerrule
\\fancypagestyle{plain}{%
$headerrule_first
\\fancyhf{} 
\\lfoot{\\today}
\\setlength{\\headsep}{0mm}
\\setlength{\\headheight}{0mm}
\\addtolength{\\footskip}{12pt}
\\addtolength{\\footskip}{5mm}
\\rfoot{", $pageString,"}}
$Config{LATEXCODE}
$Config{LATEXCODE1}
$Config{LATEXCODE2}
$Config{LATEXCODE3}
$Config{LATEXCODE4}
$Config{LATEXCODE5}
\\newlength{\\parboxwidth}
\\setlength{\\parboxwidth}{\\textwidth}
\\addtolength{\\parboxwidth}{-5pt}
\\begin{document}
\\thispagestyle{plain}",
$BeforeHeader,
"\\parbox{\\parboxwidth}{{\\large
\\begin{tabular}[t]{\@{}r>{\\raggedright}p{$PengTab}\@{}p{0mm}\@{}}";

	output \*LATEX, @PrintedMailheaders;

output \*LATEX, <<EOF;
\\end{tabular}}
\\hfill
$PengCode }
$AfterHeader
\\VerbatimInput[$Config{VERBATIMNORMAL}]{$Temp{content}}
\\setlength{\\parskip}{-1em}
$sig_tex
\\end{document}
EOF

	close LATEX;

}

##############################################################################

sub getISOlatinExtensions ($) {
	my %Extensions;
	
	$Extensions{latin159} = <<'EOF';
\DeclareInputText{165}{\textyen}
\DeclareInputText{173}{-}
\DeclareInputText{172}{\textlnot}
\DeclareInputText{174}{\textregistered}
\DeclareInputText{176}{\textdegree}
\DeclareInputText{177}{\textpm}
\DeclareInputText{178}{\texttwosuperior}
\DeclareInputText{179}{\textthreesuperior}
\DeclareInputText{181}{\textmu}
\DeclareInputText{185}{\textonesuperior}
\DeclareInputText{215}{\texttimes}
\DeclareInputText{247}{\textdiv}
EOF

	$Extensions{latin9} = <<'EOF';
\DeclareInputText{164}{\EURcr}
\DeclareInputText{180}{\v{Z}}
\DeclareInputText{166}{\v{S}}
\DeclareInputText{168}{\v{s}}
\DeclareInputText{184}{\v{z}}
\DeclareInputText{188}{\OE}
\DeclareInputText{189}{\oe}
\DeclareInputText{190}{\"{Y}}
EOF
	
	$Extensions{latin2} = <<'EOF';
\DeclareInputText{173}{-}
\DeclareInputText{176}{\textdegree}
\DeclareInputText{215}{\texttimes}
\DeclareInputText{247}{\textdiv}
EOF
	
	$Extensions{latin3} = <<'EOF';
\DeclareInputText{173}{-}
\DeclareInputText{176}{\textdegree}
\DeclareInputText{177}{\textpm}
\DeclareInputText{178}{\texttwosuperior}
\DeclareInputText{179}{\textthreesuperior}
\DeclareInputText{181}{\textmu}
\DeclareInputText{185}{\textonesuperior}
\DeclareInputText{215}{\texttimes}
\DeclareInputText{247}{\textdiv}
EOF

	$Extensions{latin4} = <<'EOF';
\DeclareInputText{173}{-}
\DeclareInputText{176}{\textdegree}
\DeclareInputText{215}{\texttimes}
\DeclareInputText{247}{\textdiv}
EOF

	$Extensions{cp1252} = <<'EOF';
\DeclareInputText{128}{\EURcr}
\DeclareInputText{130}{\quotesinglbase}
\DeclareInputText{131}{\textflorin}
\DeclareInputText{132}{\quotedblbase}
\DeclareInputText{133}{\dots}
\DeclareInputText{134}{\dag}
\DeclareInputText{135}{\ddag}
\DeclareInputText{136}{\^{}}
\DeclareInputText{137}{\textperthousand}
\DeclareInputText{138}{\v S}
\DeclareInputText{139}{\guilsinglleft}
\DeclareInputText{140}{\OE}
\DeclareInputText{142}{\v Z}
\DeclareInputText{145}{\textquoteleft}
\DeclareInputText{146}{\textquoteright}
\DeclareInputText{147}{\textquotedblleft}
\DeclareInputText{148}{\textquotedblright}
\DeclareInputText{149}{\textbullet}
\DeclareInputText{150}{\textendash}
\DeclareInputText{151}{\textemdash}
\DeclareInputText{152}{\~{}}
\DeclareInputText{153}{\texttrademark}
\DeclareInputText{154}{\v s}
\DeclareInputText{155}{\guilsinglright}
\DeclareInputText{156}{\oe}
\DeclareInputText{158}{\v z}
\DeclareInputText{159}{\"Y}
EOF
	
	$Extensions{cp1250} = <<'EOF';
\DeclareInputText{128}{\EURcr}
\DeclareInputText{130}{\quotesinglbase}
\DeclareInputText{132}{\quotedblbase}
\DeclareInputText{133}{\dots}
\DeclareInputText{134}{\dag}
\DeclareInputText{135}{\ddag}
\DeclareInputText{137}{\textperthousand}
\DeclareInputText{138}{\v S}
\DeclareInputText{139}{\guilsinglleft}
\DeclareInputText{140}{\@tabacckludge'S}
\DeclareInputText{141}{\v T}
\DeclareInputText{142}{\v Z}
\DeclareInputText{143}{\@tabacckludge'Z}
\DeclareInputText{145}{\textquoteleft}
\DeclareInputText{146}{\textquoteright}
\DeclareInputText{147}{\textquotedblleft}
\DeclareInputText{148}{\textquotedblright}
\DeclareInputText{149}{\textbullet}
\DeclareInputText{150}{\textendash}
\DeclareInputText{151}{\textemdash}
\DeclareInputText{153}{\texttrademark}
\DeclareInputText{154}{\v s}
\DeclareInputText{155}{\guilsinglright}
\DeclareInputText{156}{\@tabacckludge's}
\DeclareInputText{157}{\v t} 
\DeclareInputText{158}{\v z}
\DeclareInputText{159}{\@tabacckludge'z}
EOF

	for (shift) {
		/latin[15]/ 	&& return join("", @Extensions{qw/latin159 cp1252/});
		/latin9/		&& return join("", @Extensions{qw/latin159 cp1252 latin9/});
		/latin2/ 		&& return join("", @Extensions{qw/latin2 cp1250/});
		/latin3/ 		&& return $Extensions{latin3};
		/latin4/ 		&& return $Extensions{latin4};
		return "";
	}
} 

###################################################################################

sub getShortFrom ($$) {
	my $Header;
	my $kind_of_Header;
	my $Realname;
	
	if ($Header = shift) {
		$kind_of_Header = $String{'From'};		
	} 
	elsif ($Header = shift) {
		$kind_of_Header = $String{'To'};
	}
	else {
		return ""; 
	}

	$Realname = getRealname($Header);
	
	if ($Realname) {
		return "$kind_of_Header $Realname";
	}
	else {
		my $retVal = (split /,\s/, $Header)[0];
		$retVal =~ s/_/\\_/g;
		$retVal =~ s/-/{-}/g;
		return "$kind_of_Header $retVal";
	}
}
	

sub getRealname ($) {
	my $Header = shift;
	
	for ($Header) {

		if (/^\s*['"]*(.+?)["']*\s*<.+\@.+>/ || /^.+\@.+ \((.+)\)$/) {
			return $1;
		}

		return undef;
	}
}


##############################################################################

sub getNumberOfPages ($) {
	my $auxfile = shift;
	my $numberOfPages = 0;

	open (AUX, "$auxfile") or fatalError "Could not open $auxfile:\n$!";
	
	while (<AUX>) {
		($numberOfPages) = /\\newlabel{LastPage}{{}{(\d+)}}/;
	}
	
	close AUX or fatalError "Could not close $auxfile:\n$!";
	
	return $numberOfPages;
}

##############################################################################


sub writeFormated ($*$) {
	my $text = shift;
	my $fh = shift;
	my $wrapmargin = shift;
	$Text::Wrap::columns = $Config{WRAPMARGIN};

	output $fh, wrap("", "", $text);
}


##############################################################################

sub copyFile ($$) {
	my $src = shift;
	my $dst = shift;

	open (SRC, "<$src") or fatalError "Could not open $src for reading:\n$!";
	open (DST, ">$dst") or fatalError "Could not open $dst for writing:\n$!";
	binmode SRC;
	binmode DST;

	while (<SRC>) {
		print DST $_;
	}

	close DST or fatalError "Could not close $dst:\n$!";
	close SRC or fatalError "Could not close $src:\n$!";
}


##############################################################################

sub convertDate ($) {
	my $date = shift;

	#
	# Check if the module is installed
	eval "use Date::Parse";
	
	if ($@) {
		fatalError "It seems that you don't have installed ".
			"the module \"Date::Parse\" on your system. Please install it or ".
			"use the setting \"DATE=original\" in your ~/.muttprintrc.\n";
	}
	
	return native2utf_string(strftime($Config{DATE_FORMAT}, localtime(str2time($date))));

}

sub modifyPS ($) {

	my $Tumble;
	my $Postscript;
	my $DuplexCommand;
	
	# 
	# /Tumble true will use a short edge binding while false will use a long
	# ledge binding.
	# long edge binding makes a duplex printing looks right for portrait page.
	# short edge binding makes a duplex printing looks right for landscape
	# page.
	#

	
	if ($_[0] eq "portrait") {
		$Tumble = "false";	
	}
	elsif ($_[0] eq "landscape") {
		$Tumble = "true";
	}
	
	$DuplexCommand = <<EOF;
%%BeginFeature: *Duplex DuplexTumble

    2 dict dup /Duplex true put dup /Tumble $Tumble  put setpagedevice

%%EndFeature
EOF
	
	
	open(READ_PSFILE, "$Temp{'ps'}") or fatalError "$!";
	$Postscript = join("", (<READ_PSFILE>));	
	close READ_PSFILE;
	
	#
	# add comments that are necessary
	
	$Postscript =~ s#\%\%EndComments#$DuplexCommand#;
	
  	open(WRITE_PSFILE, "> $Temp{'ps'}") or fatalError "$!";
	output \*WRITE_PSFILE, $Postscript;
	close WRITE_PSFILE;

}

##############################################################################

sub printLog ($) {
	my $string = shift;
	open (LFILE, ">>$Temp{logf}") or die "Could not open $Temp{logf}";
	print LFILE $string;
	close LFILE or die "Could not close $Temp{logf}";
}

sub fatalError ($) {
	
	my $message = shift;
	my $hasDialog = FALSE;
	
	$Text::Wrap::columns = 70;
	$message = wrap("", "", "Line ". join " ", (caller)[1, 2] .": " .  $message);
	
	my $messageString = "Muttprint Version $VERSION -- Error\n".
		"======================================================================\n\n".
		 $message . 
		"\n\n\----------------------------------------------------------------------\n".
		"If this message does not help you, write to the maintainer of Muttprint\n".
		"(lukas.ruf\@lpr.ch) and include a detailed error description.\n".
		"Please make sure that you've read the whole documentation and checked \n".
		"for updates before you write! ******* Press Ctrl-L after terminating \n".
		"this process if the screen is not redrawed correctly!\n";

	if ($Config{DEBUG}) {
		open LOG, ">>$Temp{logf}";
		output \*LOG, $messageString;
		close LOG;
	}

	if (-x "/usr/bin/dialog") {
		$hasDialog = TRUE;
	} else {
		system("which dialog") or $hasDialog = TRUE;
	}

	unless ($runningInBackground) {
		if ($hasDialog && -t STDOUT && -t STDERR) {
			system("dialog", "--msgbox", $messageString, "22", "75");
			system("clear");
		} else {
			output \*STDERR, $messageString;
		}
	}
	exit 1;
}


sub printWarning ($) {
	
	my $message = shift;
	my $hasDialog = FALSE;
	
	$Text::Wrap::columns = 70;
	$message = wrap("", "",  $message);
	
	my $messageString = "Muttprint Version $VERSION\n".
		"======================================================================\n\n".
		 $message . 
		"\n\n\----------------------------------------------------------------------\n";

	if (-x "/usr/bin/dialog") {
		$hasDialog = TRUE;
	} else {
		system("which dialog") or $hasDialog = TRUE;
	}

	if ($hasDialog && -t STDOUT && -t STDERR) {
		system("dialog", "--msgbox", $messageString, "22", "75");
		system("clear");
	} else {
		output \*STDERR, $messageString;
	}
}
##############################################################################

{ 
	my $utf2native;
	my $native2utf;
	
	my $iconv_external;
	my $native2utf_command;
	my $utf2native_command;
	
	my $charset;
	my $needs_conversion;

	sub convert_init () {
		
		$charset = getLocaleInformation(CHARSET);
		if ($charset =~ /utf[-_]8/) {
			$needs_conversion = FALSE;
			return;
		} else {
			$needs_conversion = TRUE;
		}
		check_iconv();
		
		if (!$iconv_external) {
			if (!defined $utf2native) {
				$utf2native = Text::Iconv->new("UTF-8", $charset);
			}
			if (!defined $native2utf) {
				$native2utf = Text::Iconv->new($charset, "UTF-8");
			}
			Text::Iconv->raise_error(FALSE);
		}
	}

	sub check_iconv {
		if ($Config{ICONV_EXTERNAL} eq "on") {
			$iconv_external = TRUE;
		} else {
			$iconv_external = FALSE;
			eval "use Text::Iconv";

			if ($@) {
				fatalError "It seems that you don't have installed ".
				"the module \"Text::Iconv\" on your system. Please install it! Another ".
				"possibility is to use the external iconv on your system. ".
				"This is much slower but works. To use it, set the variable ".
				"ICONV_EXTERNAL to \"on\" in your configuration file. ".
				"It's not guaranteed that later versions of Muttprint ".
				"have this option. But internationalization of Muttprint may".
				"change in future in general.";
			}
			return;
		}

		my $try_iconv = "/usr/bin/env iconv -f $charset -t utf-8 - >> /dev/null 2>&1";
		my $try_recode = "/usr/bin/env recode $charset..utf-8a     >> /dev/null 2>&1";
		my $iconv_command = $try_iconv;

		# trying ...
		system ("echo Test | $iconv_command") and 
			( $iconv_command = $try_recode and system ("echo Test | $try_recode"))
			and fatalError "You have not installed Text::Iconv, you have no external ".
				"iconv working and you have no recode on your system. I have no idea ".
				"how to convert your characters. Giving up ...";

		if ($iconv_command eq $try_iconv) {
			$native2utf_command = "/usr/bin/env iconv -f $charset -t utf-8 -";
			$utf2native_command = "/usr/bin/env iconv -f utf-8 -t $charset -";
		} else {
			$native2utf_command = "/usr/bin/env recode $charset..utf-8";
			$utf2native_command = "/usr/bin/env recode utf-8..native";
		}
		# we finally need this for piping
		use IPC::Open2;
	}

	sub utf2native_string ($) {
		my $string = shift;

		unless ($needs_conversion) {
			return $string;
		}

		if ($iconv_external) {
			open2(\*READ, \*WRITE, $utf2native_command );
			print WRITE $string;
			close WRITE;
			return join "", <READ>;
		} else {
			return $utf2native->convert($string);
		}
	}

	sub output (*@) {
		my $fh = shift;
		my @text = @_;
		my $result;

		foreach (@text) {
			$result = utf2native_string($_);
			if (defined $result) {
				print $fh $result;
			} else {
				printLog("Error in charset conversion.".
					"String begin\n\n".  $_ . "String end\n\n" );
				fatalError "Error in charset conversion, see $Temp{logf}.";
			}
		}
	}

	sub outputstd (@) {
		output \*STDOUT, @_;
	}

	sub native2utf_string ($) {
		my $string = shift;

		unless ($needs_conversion) {
			return $string;
		}

		if ($iconv_external) {
			open2(\*READ, \*WRITE, $native2utf_command );
			print WRITE $string;
			close WRITE;
			return join "", <READ>;
		} else {
			return $native2utf->convert($string);
		}
	}

	sub input (*) {
		my $fh = shift;
		my $result;

		if (wantarray) { 
			my @text = <$fh>;
			foreach (@text) {
				$result = native2utf_string($_);
				if (defined $result) {
					$_ = $result;
				} else {
					fatalError "Error in charset conversion.\nString was\n".$_;
				}
			}
			return @text;
		} else {
			my $text = <$fh>;
			$text = $native2utf->convert($text);
			return $text;
		}
	}
}

##############################################################################



sub getDefaultPrinterCDE () {

	my $configfile = "$ENV{HOME}/.printers";
	my $printer;
	
	return "" unless (-r $configfile);

	open (CDEPRINTER, $configfile) 
		or fatalError "Could not open CDE printer configuration file:\n$!";
	
	while (<CDEPRINTER>) {
		last if (($printer) = /^_default\s+(\w*)/);
	}

	close CDEPRINTER or fatalError "Could not close CDE printer configuration file:\n$!";
	return $printer if defined $printer;
}


##############################################################################


sub setStrings () {

	my $stringsAreSet = FALSE;
	my $sharedir = findCommonDir('share');
	my $lang = getLocaleInformation(LANGUAGECODE);
	my $libdir = findCommonDir('lib');
	my (@translationDirs) = ("$sharedir/translations", $sharedir,
			"$libdir/translations", $libdir);
	
	foreach (@translationDirs) {
		if (-r "$_/translation-$lang.pl") {
			do("$_/translation-$lang.pl"); 
			$stringsAreSet = TRUE;
		}
	}

	unless ($stringsAreSet) {

$String{Usage} = <<EOF;

Usage:   muttprint [option]... [-f file]
 
Options:

PLEASE NOTICE: These options override the corresponding settings in 
~/.muttprintrc and /etc/Muttprintrc.

-h, --help
       This help.

-v, --version
       Prints the current version of Muttprint.

--print-locale
       Prints out information about the current locale environment and exits.

-f [file], --file [file]
       Reads from file instead of STDIN.

-p [printername], --printer [printername]
       Uses a specific printer.
       "-" stands for STDOUT
       For printing to a file use TO_FILE:/path/to/file

-C [print command], --printcommand [print command]
       Sets the printing command. "\$PRINTER" is substituted by the
       printer name.
       CUPS support is turned on by "CUPS" (or set it to any command
       which containes the string "\$CUPS_OPTIONS").
	 
-i [file], --penguin [file]
       Sets the picture printed on the first page.

-x, --x-face | -nox, --nox-face
       Turn printing of X-Faces on/off.

-t [number], --speed [number]
       Time in seconds which the printer needs for one page.
		   
-w [number], --wait [number]
       Time between printing odd and even pages for manual duplex printing.

-F [fontname], --font [fontname]
       Font family for printing. Possible values are:
       Latex, Latex-bright, Times, Utopia, Palatino, Charter and Bookman
	   
-H, --headrule | -noH, --noheadrule
       Turn printing of the headrule on or off.
	   
-b, --footrule | -nob, --nofootrule
       Turn printing of the footrule on or off.
	   
-S Style | --frontstyle Style
       Choose a style for the headers on the first page:
       plain, border (default), Border, fbox, shadowbox, ovalbox, Ovalbox, doublebox,
       grey, greybox. 
       Read the manual for a detailed description of this values.

-a [headers], --printed-headers [headers]
       Headers that should be printed. See manpage/manual for details.
       Example: /Date/_To_From_*Subject*

-P [paperformat], --paper [paperformat]
       Paper format: "letter" (US) or "A4" (Europe).

-e [string], --date [string]
       original: prints the date as it is in the header
       local:    converts to local time zone and language

-E [string], --date-format [string]
       date format string: see strftime(3) for details

-A [string], --addressformat [string]
       Specifies the format of the mail address in the header,
       see manpage or documentation for details.

-n [string], --verbatimnormal [string]
       Is used for setting the formating of the normal mail text. Read
       the user's guide and the manpage for details.

-V [string], --verbatimsig [string]
       Same as --verabtimnormal, but this sets the formating
       of the signature.

-D, --debug | -noD, --nodebug
       Writes useful information to a logfile /tmp/muttprint.log.

-B, --background | -noB, --nobackground
       Puts Muttprint in the background after reading the mail data.
       (prints no error messages anymore)

-d, --duplex | -nod, --noduplex
       Enables or disables duplex printing.

-g [number], --topmargin [number]
       Top margin in millimeters

-G [number], --bottommargin [number]
       Bottom margin in millimeters

-j [number], --leftmargin [number]
       Left margin in millimeters

-J [number], --rightmargin [number]
       Right margin in millimeters

-2 | -1
       Print one or two pages of text on one side of a sheet.
       Corresponds to "papersave mode".

-s, --rem_sig | -nos, --norem_sig
       Removes the signature (separated by "-- ") in the printing.

--sig_regexp [Regular expression]
       Specifies the regular expression used to recognize the signature.

-q, --rem_quote | -noq, --norem_quote
       Remove quoted paragraphs from the printed text.

-z [size], --fontsize [size]
       Font size: 10pt, 11pt, 12pt (only these values are accepted)

-W [number], --wrapmargin [number]
       Specifies the maximum length of a line before it gets wrapped.
	   
-r [file], --rcfile [file]
       Specifies an additional configuration file.

EOF

$String{"License"} = "This program is distributed under the terms of the
GPL and can be freely copied.
";

$String{"Bugs"} = "Please report bugs to <lukas.ruf\@lpr.ch>.\n";

$String{"FileNotFound"} = "The specified file was not found.\n";

@String{"From", "To", "Subject", "CC", "Date", "Newsgroups", "Organization"} =
("From:", "To:", "Subject:", "Carbon Copy:", "Date:", "Newsgroups:", "Organization:");

$String{PageOf} = "page %s of %s";

}

# backward compatibility, will be removed in future
$Config{'NEWSGROUPS_STRING'} = $Config{'NEWSGROUP_STRING'} 
			if defined $Config{'NEWSGROUP_STRING'};

	foreach ("From", "To", "Subject", "CC", "Date", "Page", "of", "Newsgroups", "Organization") {
		$String{$_}	= $Config{"\U$_"."_STRING"} if defined $Config{"\U$_"."_STRING"};
	}
}

__END__

#######################################################################################
# vim:sw=4 ts=4
