#!/usr/bin/perl

#  mytex -- Parsing LaTeX files with mixed greek and english and
#  automatically inserting proper babel commands for the language switches.
#
#  Copyright (C) 2001-2004 Christos KK Loverdos
#
#  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#  Author     : Christos KK Loverdos (loverdos@di.uoa.gr)
#  Version    : 0.2.3
#  Script  URL: http://www.di.uoa.gr/~loverdos/software/mytex
#  License URL: http://www.di.uoa.gr/~loverdos/software/gpl.txt
#
##############
#CHANGELOG   #
#####################################################################
#v0.2.3 : 2004/01/09
#- Added support for the Kerkis Font Family via --usekerkis switch
#- Added LIMITATIONS heading in documentation
#v0.2.2 : 2003/10/09
#- Added support for \section, \subsection, \subsubsection in --fixcaption
#- Added the CHANGELOG part HERE
#v0.2.1
#- minor documentation fixes
#####################################################################

use Getopt::Long;
use Pod::Usage;

=head1 NAME

mytex - Parsing B<LaTeX> files with mixed greek and english and
automatically inserting proper B<babel> commands for the language switches.

The name B<mytex> has been chosen because program names like B<grtex>, B<greektex> and
variants already existed at the time of creation.

=head1 SYNOPSIS

mytex [options] [file ...]

=head1 DESCRIPTION

B<This program> allows one to write a .tex source mixing greek and
english without the need to use LaTeX commands for selecting each
language. It will read the given input tex file(s) and produce
corresponding ones with the needed language selection commands added
automatically. It will even understand B<\includeonly>, B<\include> and
B<\input> declarations.

=head1 OPTIONS

=over 8

=item B<--verbose=level>

Print debug info while running. Available levels are
0 (no output), 1, 2 and 3. The default level is 1.

=item B<--program=prg>

Which program to use (e.g. B<tex>, B<latex>, B<lambda>)
on the given files. The default is B<tex>. If you do not want anything,
just say B<--program=0>

=item B<--programtimes=n>

How many times to run program (usually latex). The default value is 1.
The option is useful when several passes are needed on the input file(s).

=item B<--shortescapes>

The program uses shorter versions of the \textgreek and \latintext macros.
At present they are \tg and \lt repsectively but in the future they should be 
user-specified. The default option used is the opposite: --noshortescapes.

=item B<--babel=lang>

Which language to use when using package B<babel>.
The default is B<greek>. 
It can be a comma separated list of
languages (e.g. B<greek,english>).

=item B<--asmany>

If present, it tries to match as many adjacent greek characters 
as possible, including spaces and other characters given by the
B<--greekchars> option. This will drastically produce more 
compact output.

=item B<--greekchars=chars>

The B<chars> given are also treated as greek. The default value is
",.():" (without the quotes)

=item B<--output=ps|pdf>

If the program used created a .dvi file, this is used to produce a .ps
(using B<dvips>) or .pdf (using B<ps2pdf>).

=item B<--cleanfiles|--nocleanfiles>

Used to deal with intermediate (including *.tex) files.
--cleanfiles is the default operation which removes any
files generated by this program.

=item B<--fixcaption=lang-before,lang-after>

Inserts a \selectlanguage{lang-before} before every \caption and
a \selectlanguage{lang-after} after every \caption{. Any of the two
can be ommited, but if it is <lang-before> then the comma is needed.

=item B<--fixchapter=lang-before,lang-after>

Like --fixcaption, but for \chapter, \section, \subsection, \subsubsection.

=item B<--fixTOC=lang>

Inserts \selectlanguage{lang} before \tableofcontents

=item B<--fixtoday=lang>

Inserts \selectlanguage{lang} before \today

=item B<--noautofixtoday>

When we do not want to automatically insert a correct \selectlanguage{}
command before \today. Otherwise, if --babel=greek then
a \selectlanguage{greek} is inserted before every \today. This insertion fixes
a bug with B<babel> that doesnot handle properly a \date for greek (it
outputs greeklish instead of greek).

=item B<--usebibtex>

Runs bibtex on the input.
After bibtex it runs twice again the user B<--program>

=item B<--usekerkis>

Uses the Kerkis Font Family, developed by A. Syropoulos and A. Tsolomitis,
reached at http://iris.math.aegean.gr/kerkis/

=back

=head1 VERSION

0.2.3

=head1 BUGS

Reported to occasionally eat parts of the preamble...

=head1 LIMITATIONS

Plenty. Supports only the ISO-8859-7 encoding, for the obvious laziness-related reasons.

=head1 DOCUMENTATION

To get an HTML version of this, use B<pod2html>

=head1 URL

http://www.di.uoa.gr/~loverdos/software/mytex

=head1 LICENSE

This software is released under the GNU Public License. You can
have a copy of it from
http://www.di.uoa.gr/~loverdos/software/gpl.txt

=head1 AUTHOR

Christos KK Loverdos (loverdos@di.uoa.gr)

=cut

$GREEK="\xA2\xB6\xB8-\xBA\xBC\xBE-\xFE";
$SUFFIX = '.my.tex';

$TEXTGREEK='\textgreek';
$LATINTEXT='\latintext';
$langescapes='';

$header =<<EOH;
% +mytex: Automatic Header Generation
\\usepackage[--LANG--]{babel}
\\usepackage[iso-8859-7]{inputenc}
--USE-KERKIS--
--LANGESCAPES--
% -mytex: Automatic Header Generation
EOH

@cleanlist=();

sub verbose
{
	return if($verbose < shift);

	print '****', ' 'x(4*$nesting+1);
	foreach(@_)
	{
		print "$_";
	}

	print "\n";
}

sub delta
{
	my ($a,$b)=@_;

	return $a==0? 0 : int(100 * ($b - $a) / $a);
}

sub doFile
{
	my ($file) = @_;

	if(-f $file)
	{
		if($file !~ /$SUFFIX$/)
		{
			verbose 2, "Processing  : $file";
			return processFile($file);
		}
		else
		{
			verbose 0, "Warning: File $file seems already processed, ignoring it";
		}
	}
	else
	{
		verbose 0, "Warning: $file doesnot exist.";
	}

	"";
}

sub fixName
{
	return $_[0] if($_[0] =~ /\.tex$/);
	return $_[0] . ".tex";
}

sub newName
{
	fixName($_[0]) . $SUFFIX;
}

sub newNameNoTex
{
	my $x = newName($_[0]);
	$x =~ s/\.tex$//;
	$x;
}

sub processFile
{
	my ($name) = @_;
	my $contents='';
	open TEX, "$name";
	$contents = join "", (<TEX>);
	close TEX;

	my $bytes_0 = length $contents;
	$totalbytes_0 += $bytes_0;

	# fix caption (before & after)
	$contents =~ s/\\caption/\\selectlanguage{$fixcaption_before}\\caption/sg if($fixcaption_before); 
	$contents =~ s/(\\caption\s*\{)/$1\\selectlanguage{$fixcaption_after}/sg if($fixcaption_after); 

	# fix chapter (before & after)
	# Added: 2003/10/09: also support for section, subsection, subsubsection
	$contents =~ s/\\(chapter|(sub(sub)?)?section)/\\selectlanguage{$fixchapter_before}\\$1/sg if($fixchapter_before); 
	$contents =~ s/(\\(chapter|(sub(sub)?)?section)\s*\{)/$1\\selectlanguage{$fixchapter_after}/sg if($fixchapter_after); 

	# fix TOC (Table of Contents)
	$contents =~ s/\\tableofcontents/\\selectlanguage{$fixTOC}$&/s if($fixTOC);

	# fix \today
	$contents =~ s/\\today/\\selectlanguage{$fixtoday}$&/sg if($fixtoday);

	# remove declared <babel> support
	$contents =~ s/\\usepackage(\[.+?\])?{babel}//s;

	# remove declared <inputenc> support
	$contents =~ s/\\usepackage(\[.+?\])?{inputenc}//s;

	# v0.2.3
	# remove declared <kerkis> support (to be added again if --usekerkis)
	$contents =~ s/\\usepackage(\[.+?\])?{kerkis}//s;

	# add <babel> and <inputenc> support (v0.2.3 and kerkis)
	$contents =~ s/(.*?)(\s*\\documentclass(?:\[.+?\])?\{.+?\})(\n.+)/$1$2\n\n$header$3/s;

	# handle greek text
	if($asmany)
	{
		#$contents =~ s/([\xC1-\xFF]([0-9\xC1-\xFF\:\.\/ ]*[\xC1-\xFF])?)/$TEXTGREEK{$1}$LATINTEXT{}/sg;
		$contents =~ s/([$GREEK]([0-9$GREEK$alsoGREEK ]*[$GREEK$alsoGREEK])?)/$TEXTGREEK\{$1\}$LATINTEXT\{\}/sg;
	}
	else
	{
		#$contents =~ s/([\xC1-\xFF][0-9\xC1-\xFF\:\.\/]*)/$TEXTGREEK{$1}$LATINTEXT{}/sg;
		$contents =~ s/([$GREEK][0-9$GREEK$alsoGREEK]*)/$TEXTGREEK\{$1\}$LATINTEXT\{\}/sg;
	}

	# handle <includeonly>
	if($contents =~ /\\includeonly([^{]*){([^}]+)}/mi)
	{
		my $pre = $`;
		my $suf = $';

		my $includes = $2;
		$includes =~ s/\s*[%]+.*\n//g;
		$includes =~ s/\n//mg;
		my @includes = split ',', $includes;

		my @newincludes = ();
		foreach my $on(@includes)
		{
			my $nn = newNameNoTex($on);
			verbose 2, "IncludeOnly : $on -- > $nn";
			push @newincludes, $nn;
		}

		$includes = join ",\n", @newincludes;

		$contents = "$pre\\includeonly{\n$includes}$suf"; 
	}

	# handle <input> files

	sub fixInput
	{
		sub fixIt
		{
			my ($name, $what) = @_;
			my $on = $name;
			my $nn = newNameNoTex($on);
			verbose 2, "Changing    : \\$what"."{$on} --> \\$what"."{$nn}";
			$on = fixName($on);
			verbose 2, "Reading file : $on";
			#my $newnest = $nesting + 1;
			#verbose 3, "Executing   :$0 --nesting $newnest $on";
			#my $res = qx"$0 --verbose $verbose --nesting $newnest $on";
			#chomp $res;
			#verbose 1, $res;
			$nesting++;
			doFile $on;
			$nesting--;
			return $nn;
		}

		my ($contents) = @_;

		if($contents =~ /\\input{([^}]+)}/)
		{
			my $nn = fixIt($1, "input");
			$contents = fixInput($`) . "\\input{$nn}" . fixInput($');
		}
		elsif($contents =~ /\\include{([^}]+)}/)
		{
			my $nn = fixIt($1, "include");
			$contents = fixInput($`) . "\\include{$nn}" . fixInput($');
		}

		return $contents;
	}

	$contents = fixInput($contents);

	my $bytes_1 = length $contents;
	$totalbytes_1 += $bytes_1;

	$name = "$name$SUFFIX";
	open TEX, ">$name";
	print TEX $contents;
	close TEX;

	# add to cleanlist
	push @cleanlist, "$name";

	verbose 3, "Initial length: $bytes_0";
	verbose 3, "Final length:   $bytes_1";
	verbose 1, "Writing file: $name (+", delta($bytes_0, $bytes_1), "%)";

	return $name;
}

$alsoGREEK=",.():";
$program='hugelatex';
$programtimes=1;
$help=0;
$verbose=1;
$asmany=0;
$nesting=0;
$babel='greek';
$greekchars='';
$shortescapes=0;
$output='dvi';
$cleanfiles=1;
$fixcaption='';
$fixcaption_before='';
$fixcaption_after='';
$fixchapter='';
$fixchapter_before='';
$fixchapter_after='';
$fixTOC='';
$usebibtex=0; # 2002/03/03
$fixtoday=0;  # 2002/03/07
$autofixtoday=1;
$usekerkis=''; # 2004/01/09

GetOptions(
	'program=s' => \$program,
	'programtimes=i' => \$programtimes,
	'verbose:i' => \$verbose,
	'help+' => \$help,
	'asmany+' => \$asmany,
	'nesting=i' => \$nesting,
	'babel|language=s' => \$babel,
	'greekchars=s' => sub { my ($name, $val) = @_; $alsoGREEK .= $val},
	'shortescapes!' =>\$shortescapes,
	'output=s' => \$output,
	'cleanfiles!' => \$cleanfiles,
	'fixcaption=s' => \$fixcaption,
	'fixchapter=s' => \$fixchapter,
	'fixTOC=s' => \$fixTOC,
	'fixtoday=s' => \$fixtoday,
	'autofixtoday!' => \$autofixtoday,
	'usebibtex+' => \$usebibtex,
	'usekerkis+' => \$usekerkis, # Note: not sure whether correct (haven't touched perl for a long time...)
);

($help or $#ARGV==-1)  and pod2usage(-verbose => 2);
($fixcaption_before, $fixcaption_after) = split ',', $fixcaption;
($fixchapter_before, $fixchapter_after) = split ',', $fixchapter;
$usekerkis = '\\usepackage{kerkis}' if($usekerkis);

# There is a problem with \today with greek
if($autofixtoday && !$fixtoday)
{
	$fixtoday = ($babel eq 'greek') ? 'greek' : 0;
}

#verbose 2, "$0 -v -p $program --nesting $nesting --babel=$babel ", ($asmany?"--asmany ":" "), (join ' ', @ARGV);

if($shortescapes)
{
	$TEXTGREEK='\tg';
	$LATINTEXT='\lt';
$langescapes=<<EOH;
\\newcommand{\\lt}[0]{\\latintext}
\\newcommand{\\tg}[1]{\\textgreek{#1}}
EOH
}

$header =~ s/--LANG--/$babel/;
$header =~ s/--LANGESCAPES--/$langescapes/;
$header =~ s/--USE-KERKIS--/$usekerkis/; # v0.2.3

foreach $file(@ARGV)
{
	$totalbytes_0 = 0;
	$totalbytes_1 = 0;

	my $nn = doFile($file);

	verbose 1, "$file: Total bytes read   : $totalbytes_0";
	verbose 1, "$nn: Total bytes written: $totalbytes_1 (+", delta($totalbytes_0, $totalbytes_1), "%)";

	next if(!$program || !$nn || !$programtimes);

	for(my $i=1; $i<=$programtimes; $i++)
	{
		verbose 1, "";
		verbose 1, "Running $program on $nn, pass $i";
		verbose 1, "";
		system("$program $nn");
	}

	$nn =~ s/\.tex$//;

	# use BibTeX if requested
	if($usebibtex)
	{
		verbose 1, "";
		verbose 1, "Running bibtex on $nn";
		system("bibtex $nn");
		verbose 1, "Running again $program after bibtex (1)";
		system("$program $nn");
		verbose 1, "Running again $program after bibtex (2)";
		system("$program $nn");

	}

	push @cleanlist, "$nn.aux";
	push @cleanlist, "$nn.log";
	push @cleanlist, "$nn.lot";
	push @cleanlist, "$nn.lof";
	push @cleanlist, "$nn.toc";
	push @cleanlist, "$nn.out" if($program eq 'pdflatex'); # 2002/02/27
	push @cleanlist, "$nn.bbl" if($usebibtex);
	push @cleanlist, "$nn.blg" if($usebibtex);

	# only if a .dvi file exists
	my $dvi = "$nn.dvi";
	if(-f $dvi and ($output eq 'ps' or $output eq 'pdf'))
	{
		my $ps  = "$nn.ps";

		verbose 1, "";
		verbose 1, "Making $ps from $dvi";
		verbose 1, "";
		system("dvips -o $ps $dvi");

		push @cleanlist, $dvi;

		if($output eq 'pdf')
		{
			my $pdf = "$nn.pdf";

			verbose 1, "";
			verbose 1, "Making $pdf from $ps";
			verbose 1, "";
			system("ps2pdf $ps $pdf");

			push @cleanlist, $ps;
		}
	}

	next if(!$cleanfiles);
	verbose 1, "Cleaning temporaries ";
	unlink @cleanlist;
}
