Created
August 8, 2022 16:40
-
-
Save barn/d3c6411ed2b97e276c07fdf141bf76e4 to your computer and use it in GitHub Desktop.
messy hacked version of muttprint
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 <[email protected]> # | |
# Copyright (c) 2005, Lukas Ruf <[email protected]> # | |
# # | |
# 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 => 'off', | |
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 => 'on', | |
); | |
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,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) { | |
$utf2native_command = "iconv -f UTF-8 -t 'UTF8-MAC' "; | |
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment