* Globe Trotter via Mutt-users <mutt-users@mutt.org> [2021-11-22 03:48]: > What is the recommended way to pretty-print mutt > emails? I found a sourceforge perl script called > muttprint but that was last updated in 2008, and I was > wondering what folks here recommended?
I use muttprint without problems. Find it attached, as maybe I have changed something in the script, you can make the diff. Then I have this settings in ~/.mutt/defaults which I load from ~/.mutt/muttrc set print_command="/home/data1/protected/bin/muttprint_print.sh" And the script `muttprint_print.sh' is attached as well. -- Jean Take action in Free Software Foundation campaigns: https://www.fsf.org/campaigns In support of Richard M. Stallman https://stallmansupport.org/
#!/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.wa...@gmx.de> # # Copyright (c) 2005, Lukas Ruf <lukas....@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
muttprint_print.sh
Description: Bourne shell script