#!/usr/bin/perl -w
#
# texprL3: print ISO-Latin-3 text files by (ab)using TeX.
#
# Copyright (C) 1999 Albert Reiner. All rights reserved. This program is
# free softare; you can redistribute it and/or modify it under the same
# terms as Perl itself.
#
# See the file README for notes on installation. Use perldoc to view the
# embedded documentation.

=pod

=head1 NAME

texprL3 - program to print out Latin-3 texts via TeX.

=head1 SYNOPSIS

B<texprL3> [C<--config> I<file>] [C<--help>] [C<--inspect>] [C<--name>
I<string>] [C<--tex> I<command>] [C<--print> I<command>]

=head1 DESCRIPTION

For those of us who have or want to deal with a language that cannot
be properly represented by 7-bit ASCII or the ISO-Latin-1 character
set, a peculiar and somewhat annoying situation may arise: even
though it is no problem to view or edit unformatted text in another
character set supported by the system, it is sometimes difficult to
obtain a decent printout that is readable without extra guessing
effort. B<texprL3> is meant to somewhat alleviate this situation: this
Perl script will happily accept your ISO-Latin-3 texts via F<stdin>
and print them by (ab)using TeX.

Or at least that is what it is meant to be. There are, however, two
problems: (1) the author does not know (nor, in some cases, care
about) all the characters in the set; especially character codes 128
to 159 remain mysterious to him. And (2) there are no commands in
plain TeX (and only that TeX-format is to be used here to retain
compatibility and portability) for some characters like, e. g.,
character number 164.

But these needn't be problems for you: For if you know how to get your
installation of plain TeX (possibly in collaboration with Metafont) to
print a character you need, you only have to add one line to your
configuration file.

By the same token, if you want to use this program for a character set
other than ISO-Latin-3, you will easily be able to modify the script
to suit your needs. (But, please, give the resulting script a new name
reflecting the character set.)

=head2 How the script works

Normal operation proceeds through the following steps:

=over 4

=item *

Read any command line arguments (see section on B<Options> below).

=item *

Read the configuration file (if any). Actually, the configuration file
is a valid Perl script that is called via C<do()>. For more
information and a short discussion of security related issues, see the
section on the B<Configuration file> below.

=item *

Create a working directory. Unless specified otherwise, the default is
the PID appended to C</tmp/texprL3-job.>. In normal operation, this
directory will be removed at the end of the program.

=item *

Now the most important part: store the regular expressions needed for
the translation of characters to TeX code: First for the characters
explicitly implemented in the program; then any additional regular
expressions defined in the configuration file; and then a generic
replacement for any remaining characters.

=item *

Call TeX and send initialization commands.

=item *

Now read the text, a line at a time, from F<stdin>, and send the
appropriate commands (created by applying the substitution command
with the regular expressions mentioned above) to TeX.

=item *

Say C<\bye> to TeX.

=item *

Print the resulting C<texput.dvi>-file.

=item *

Finally, get rid of the working directory.

=back

As you can see from this short description, apart from changing the
script there are two ways to alter the behavior of B<texprL3>: command
line options and the configuration file. Both allow more than just
simple customization to make the printout suit your needs, but of
course the configuration file is the more powerful tool: not only does
it give you the possibility to do anything you can achieve with Perl
with your permissions, but also it is input after the processing of
the command line options and thus can override those.

=cut


################################################################
#
# Command line arguments:
#

=pod

=head1 OPTIONS

Currently B<texprL3> supports the options documented below; if you
omit them, reasonable default values will be used for a number of
them. You should nevertheless always bear in mind that a configuration
file may override any of those settings.

=over 4

=cut

sub GetOption
{
    unless (defined($ARGV[++$i]))
    {
	die "Option $ARGV[$i-1] does not seem to be complete.\n";
    };
    $ARGV[$i];
};

for ($i = 0; $i <= $#ARGV; $i++)
{
    if ("$ARGV[$i]" eq "--config")
    {
=pod

=item C<--config> I<file>

Read I<file> as the configuration file. If this option is not given,
the following alternatives are tried and the first file found is read:
(1) a file named by the environment variable I<TEXPRLATIN3_RC>; (2) a
user-specific configuration file F<~/.texprL3rc>; (3) a systemwide
configuration file F</etc/texprL3rc>. If none of these is found,
default values that the author deems reasonable are used.

=cut
	$config="$ARGV[++$i]";
    }
    elsif ("$ARGV[$i]" eq "--help")
    {
=pod

=item C<--help>

Print out a little usage summary.

=cut
	print <<"ASDF";
Usage: ... | $0 [Options]
or     $0 [Options] < file
or     $0 [Options]   (if you want to print out what you type)

Valid options are:
--config file    Read file for configuration and additional commands.
--help           Print out this message.
--inspect        Do not print, keep temporary files.
--name string    Assign string to variable $name for use in
                 configuration file.
--tex command    Use command for calling TeX.
--print command  Use command for printing file $workDir/texput.dvi.

Note that configuration files may not honor some of these command
line arguments.
ASDF
    exit 0;
    }
    elsif ("$ARGV[$i]" eq "--inspect")
    {
=pod

=item C<--inspect>

Do not print nor remove the files generated by TeX in the working
directory. At the end of the run you will be informed in what
directory you can find the files F<texput.log> and F<texput.dvi>.

=cut
	$inspect = 0;
    }
    elsif ("$ARGV[$i]" eq "--name")
    {
=pod

=item C<--name> I<string>

Set the variable C<$name> to the given string. This may be used in the
initialization file, e. g. in order to produce some page
headers. B<texprL3> itself, however, will not use this information.

=cut
	$name = &GetOption;
    }
    elsif ("$ARGV[$i]" eq "--tex")
    {
=pod

=item C<--tex> I<command>

Use I<command> to call TeX. Default value is constructed by appending
C<E<gt>/dev/null> to the output of C<which tex>.

=cut
	$TeX = &GetOption;
    }
    elsif ("$ARGV[$i]" eq "--print")
    {
=pod

=item C<--print> I<command>

Use I<command> to print the file C<$workDir/texput.dvi>. Default:
C<lpr $workDir/texput.dvi>.

=cut
	$printDvi = &GetOption;
    }
    else
    {
	die <<"ASDF";
Illegal option "$ARGV[$i]". Type "$0 --help" for more information.
ASDF
    };
};


################################################################
#
# Read configuration file:
#

=pod

=head1 CONFIGURATION FILE

You may find it necessary or desirable to adapt B<texprL3>'s behaviour
by creating a configuration file. As mentioned above, this file
contains valid Perl code that will be called via Perl's C<do()> and is
meant to set a number of variables, even though you are not restricted
to this. Where this makes sense, however, reasonable default values
are provided that you will be able to find in the script itself. In
this context you should however note that B<texprL3> was meant to
produce listings or other plain text; hence the decision to use C<\tt>
for the font and to output some representation of all characters
rather than drop them if a faithful representation in TeX is missing.

=head2 Variables the main program will use

The following lists the variables you might want to set for the main
program. Note however that some of these variables can also be set
from the command line. If you do not want to override those settings,
you should say something like, e.g., C<unless(defined(>I<variable>C<))
{>I<variable>C<=>I<something>C<};>.

=over 4

=cut

unless (defined($config))
{
    if (defined($ENV{TEXPRLATIN3_RC})) { $config=$ENV{TEXPRLATIN3_RC}; }
    elsif (-r <~/.texprL3rc>) { $config=<~/.texprL3rc> }
    elsif (-r "/etc/texprL3rc") { $config="/etc/texprL3rc" };
};

if (defined($config)) { do ("$config") };

################################################################
#
# Create working directory:
#

=pod

=item C<$workDir>

You can set the working directory via this variable (you do not need a
trailing C</>). Be careful that the directory can be created and that
you do not assign the same directory to two jobs. See also
C<$workDirBase>.

=item C<$workDirBase>

You can use this variable to set the working directory to
C<$workDirBase> with the process ID appended. Default:
C</tmp/texprL3-job.>. See also C<$workDir>.

=cut

unless (defined($workDir))
{
    unless (defined($workDirBase)) 
    {
        $workDirBase = "/tmp/texprL3-job."
    };
    $workDir = $workDirBase . "$$";
}
mkdir ($workDir, 0700) ||
    die "$0 cannot create directory `$workDir'.\n";

$oldDir=`pwd`;
chomp($oldDir);
chdir($workDir) ||
    die "$0 cannot enter directory `$workDir'.\n";
$workDir=`pwd`;           # get absolute path
chomp($workDir);

################################################################
#
# Set up regular expressions:
# order is important, so we don't use a hash.
#

# Special signs we will need for TeX-macros:
# ( workaround using $$ for { and } )
push(@regexp, ['\\\\', '\\\\']);
push(@regexp, ['\$', '\\$']);
push(@regexp, ['\{', '.$$\\tt\\char$$\\{$$.']);  # {}-workaround
push(@regexp, ['\}', '.$$\\tt\\char$$\\}$$.']);  # {}-workaround
push(@regexp, ['\\.\\$\\$', '{']);               # {}-workaround
push(@regexp, ['\\$\\$\\.', '}']);               # {}-workaround
# Accents:
push(@regexp, ['\'', '{\\\'{}}']);
push(@regexp, ['\^', '{\\^{}}']);
push(@regexp, ['`', '{\\`{}}']);
push(@regexp, [chr(168), '{\\"{}}']);
push(@regexp, ['\\$\\$', '`']);                  # {}-workaround
# Non-printing characters: CTRL-codes (apart from ^?), ESC, and Space:
foreach $i (0..8, 11..26, 29, 31)
  { push(@regexp, [chr($i), '\\cntrl{' . chr($i + 64) . '}'] ) };
push(@regexp, [chr(9), '{\\TAB}']);
push(@regexp, [chr(27), '{\\ESC}']);
push(@regexp, [chr(28), '\\cntrl{\\\\}']);
push(@regexp, [chr(30), '\\cntrl{\\^{}}']);
push(@regexp, [chr(32), '\\ ']);
# Esperanto:
push(@regexp, ['', '{\\^C}']);
push(@regexp, ['', '{\\^c}']);
push(@regexp, ['', '{\\^G}']);
push(@regexp, ['', '{\\^g}']);
push(@regexp, ['', '{\\^H}']);
push(@regexp, ['', '{\\^h}']);
push(@regexp, ['', '{\\^J}']);
push(@regexp, ['', '{\\^\\j}']);
push(@regexp, ['', '{\\^S}']);
push(@regexp, ['', '{\\^s}']);
push(@regexp, ['', '{\\u U}']);
push(@regexp, ['', '{\\u u}']);
# Other characters:
push(@regexp, [chr(35), '\\#']);
push(@regexp, [chr(37), '\\%']);
push(@regexp, [chr(38), '\\&']);
push(@regexp, [chr(95), '\\_']);
push(@regexp, ['\|', '{\\tt|}']);
push(@regexp, [chr(126), '{\\~{}}']);
push(@regexp, [chr(127), '\\cntrl{?}'] );
push(@regexp, [chr(163), '{\\it\\$}']);
push(@regexp, [chr(167), '{\\S}']);
push(@regexp, [chr(176), '{${}^\circ$}']);
push(@regexp, [chr(177), '{$\\hbar$}']);
push(@regexp, [chr(192), '{\\`A}']);
push(@regexp, [chr(193), '{\\\'A}']);
push(@regexp, [chr(194), '{\\^A}']);
push(@regexp, [chr(196), '{\\"A}']);
push(@regexp, [chr(200), '{\\`E}']);
push(@regexp, [chr(201), '{\\\'E}']);
push(@regexp, [chr(202), '{\\^E}']);
push(@regexp, [chr(203), '{\\"E}']);
push(@regexp, [chr(204), '{\\`I}']);
push(@regexp, [chr(205), '{\\\'I}']);
push(@regexp, [chr(206), '{\\^I}']);
push(@regexp, [chr(207), '{\\"I}']);
push(@regexp, [chr(209), '{\\~N}']);
push(@regexp, [chr(210), '{\\`O}']);
push(@regexp, [chr(211), '{\\\'O}']);
push(@regexp, [chr(212), '{\\^O}']);
push(@regexp, [chr(214), '{\\"O}']);
push(@regexp, [chr(217), '{\\\'U}']);
push(@regexp, [chr(218), '{\\\`U}']);
push(@regexp, [chr(219), '{\\^U}']);
push(@regexp, [chr(220), '{\\"U}']);
push(@regexp, [chr(223), '{\\ss}']);
push(@regexp, [chr(224), '{\\`a}']);
push(@regexp, [chr(225), '{\\\'a}']);
push(@regexp, [chr(226), '{\\^a}']);
push(@regexp, [chr(228), '{\\"a}']);
push(@regexp, [chr(232), '{\\`e}']);
push(@regexp, [chr(233), '{\\\'e}']);
push(@regexp, [chr(234), '{\\^e}']);
push(@regexp, [chr(235), '{\\"e}']);
push(@regexp, [chr(236), '{\\`\\i}']);
push(@regexp, [chr(237), '{\\\'\\i}']);
push(@regexp, [chr(238), '{\\^\\i}']);
push(@regexp, [chr(239), '{\\"\\i}']);
push(@regexp, [chr(241), '{\\~n}']);
push(@regexp, [chr(242), '{\\`o}']);
push(@regexp, [chr(243), '{\\\'o}']);
push(@regexp, [chr(244), '{\\^o}']);
push(@regexp, [chr(246), '{\\"o}']);
push(@regexp, [chr(249), '{\\\'u}']);
push(@regexp, [chr(250), '{\\\`u}']);
push(@regexp, [chr(251), '{\\^u}']);
push(@regexp, [chr(252), '{\\"u}']);
# Add user-defined regular expressions:

=pod

=item C<@RegExp>

You can add replacement rules to those already built in. If you want
to replace, say, I<regexp> by I<tex-code>, simply include the
statement C<push(@RegExp, ['>I<regexp>C<', '>I<tex-code>C<']);> in your
configuration file; I<regexp> and I<tex-code> will then be used in a
substitution statement with C<s/>I<regexp>C</>I<tex-code>C</g>, so that case
is significant. Furthermore it turns out that you cannot use C<\1>, C<\2>,
etc. in I<tex-code>.

In defining additional replacements, a backslash in the TeX-code
produced must be given by two backslashes. Furthermore you should pay
special attention to the necessary escaping: the strings will be
processed both when you define them and when the substitution is
performed; if you do not see why something doesn't work, try to
understand the rather not-so-obvious statement C<push(@regexp,
['\\\\', '\\\\']);> first; also the C<--inspect> option lets you see
in the log-file what TeX actually received. You should be careful not
to lose any whitespace (unless that is exactly what you want).

There is no reason why I<regexp> should only be restricted to single
characters; you might, e. g., want to replace any occurence of C<TeX>
by the control sequence C<\TeX>. If you want to do this, use C<TeX>
for I<regexp> and C<{\\TeX}> for I<tex-code>.

= cut

if (@RegExp) { @regexp = ( @regexp, @RegExp ) };
# And mark the rest as unknown characters:
LOOP: foreach $c (128..255)
{
    foreach $r (@regexp)
    {
	next LOOP if (chr($c) =~ m/$$r[0]/);
    };
    push(@regexp, [chr($c), "\\unknown{$c}"]);
};

################################################################
#
# Print to TeX:
#

=pod

=item C<$TeX>

B<texprL3> expects to be able to use plain TeX by piping its output to
C<$TeX>. You probably want to avoid seeing TeX's responses on the
screen.  Assuming that you can call plain TeX with the simple command
"tex", the default of the script, i. e. the output of C<which tex>
with redirection of output to F</dev/null>, will work for you.

In case your installation of TeX expects to read the format from
F<stdin>, you will have to set this up in C<$texSetup>; if you want to
get the TeX code in a file rather than to pass it to TeX's F<stdin>,
you can send B<texprL3>'s output to a file by specifying, e.g., C<cat
E<gt> F</absolute/file/name>> (if you want to use a relative file name,
you should take into account that it will be interpreted relative to
the working directory).

See also the command line option C<--tex>.

=cut

# Where is (plain) TeX?
unless (defined($TeX))
{
    $TeX = `which tex`;
    chomp($TeX);
    $TeX = $TeX . " > /dev/null";
};
chomp $TeX;

# Open and initialise TeX:
open(TEX, "| $TeX");
print TEX "\\relax\n";

# Define: \\ to print a single \:
print TEX "\\def\\\\{{\\tt\\char`\\\\}}\n";

# CTRL-characters:
=pod

=item C<$defCntrl>

This command is directly sent to TeX in order to define a macro
C<\cntrl#1> to set Control-codes. Default:
C<\def\cntrl#1{\hbox{\it\^{}#1}}>.

=cut
unless (defined($defCntrl))
    { $defCntrl='\\def\\cntrl#1{\\hbox{\\it\\^{}#1}}' };

# ESC:
=pod

=item C<$defESC>

This is analogous to C<$defCntrl> but for ESC. Default:
C<\def\ESC{\hbox{\it ESC}}>.

=cut

unless (defined($defESC)) { $defESC='\\def\\ESC{\\hbox{\\it ESC}}' };

# TAB:
=pod

=item C<$defTAB>

This is analogous to C<$defESC>, but for TAB. Default:
C<\def\TAB{\hbox{\it TAB}}>.

=cut

unless (defined($defTAB)) { $defTAB='\\def\\TAB{\\hbox{\\it TAB}}' };

# Characters marked unknown:
=pod

=item C<$defUnknown>

When B<texprL3> encounters a character for which it knows no suitable
TeX-replacement (cf. the remarks at the beginning of the section
entitled B<Description>), it calls the macro C<\unknown#1> with the
character code (decimal). This macro must have been defined by sending
C<$defUnknown> to TeX. Default: C<\def\unknown#1{\hbox{\it#1}}>.

=cut

unless (defined($defUnknown))
    { $defUnknown='\\def\\unknown#1{\\hbox{\\it#1}}' };

# Blank line:

=pod

=item C<$defBlankline>

B<texprL3> is meant to print out a plain text file without any
additional formatting. This means, among other things, that a sequence
of empty lines must not be treated like a single empty line, as TeX
would normally do. C<$defBlankline> defines a macro that will be
called to set a blank line. Default: C<\def\blankline{\hbox{}\par}>.

=cut

unless (defined($defBlankline))
    { $defBlankline = '\\def\\blankline{\\hbox{}\\par}' };

# Additional initialisation of TeX:
=pod

=item C<$texSetup>

This variable's content, too, will be sent directly to TeX for
initialization.  Default: C<\tt\parindent0pt\tolerance50000>. You
might, e.g., set up page headers using the name specified by the
C<--name> option here; C<\nopagenumbers> is probably also a good
candidate.

=cut

unless (defined($texSetup)) {$texSetup =<<"ADSF"};
\\tt\\parindent0pt
\\tolerance50000
ADSF

# Write the above definitions:
foreach $l ($defCntrl, $defESC, $defTAB, $defUnknown, $defBlankline, $texSetup)
    {
	chomp ($l);
	print TEX "$l\n";
    };

# To be prepended to every line:

=pod

=item C<$preLine>

When sending to TeX, C<$preLine> will be prepended to every non-blank
line. You might specify, e.g., C<\line{> if you didn't want long lines
to be broken, or you might specify some more elaborate macro for
handling TABs. Beware of indentations! Default: C<\noindent{}>.

=cut

unless (defined($preLine)) { $preLine = '\\noindent{}' };

# To be appended to every line, e. g. '}':

=pod

=item C<$postLine>

When sending to TeX, C<$postLine> will be appended to every non-blank
line. You might specify, e.g., a closing C<}> if you opened a
parenthesis in C<$preLine>. Default: C<\par>.

=cut

unless (defined($postLine)) { $postLine = "\\par" }; 

# Write to TeX:
while (<STDIN>)
{
    chomp;
    if (length($_) == 0)
    {
	print TEX "\\blankline\n";
    }
    else 
    {
	foreach $re (@regexp)
	{
	    $_ =~ s/$$re[0]/$$re[1]/g;
	};
	print TEX "$preLine$_$postLine\n";
    };
};

# Close TeX:
print TEX "\\bye\n";
close TEX;

################################################################
#
#   Print DVI-file or report problem:
#

# Command to print a DVI-file:
=pod

=item C<$printDvi>

B<texprL3> will simply C<eval()> the content of C<$printDvi> in order
to print the file C<$workDir>F</texput.dvi>. You might want to specify
C<cp $workDir/texput.dvi> I<destination> to only get a copy of the dvi
file in I<destination>, or maybe to use C<dvips $workDir/texput.dvi> C<-o>
C<!lpr 2E<gt> /dev/null> if C<lpr> cannot handle dvi-files
properly. Default: C<lpr $workDir/texput.dvi>.

=cut

unless (defined($printDvi)) { $printDvi = 'lpr $workDir/texput.dvi' };

# Print or report a problem:

=pod

=item C<$inspect>

If C<$inspect> is defined, this has the same effect as calling
B<texprL3> with the option C<--inspect>.

=cut

if (defined($inspect))
{
    die "Your TeX-files are now in directory $workDir/.\n";
}
else
{
    if (-e "$workDir/texput.dvi")
    {
	eval 'system ("' . $printDvi . '")';
    }
    else          # We've got a problem!
    {
	die "When called by $0, TeX did not produce any output.\n";
    };
};

################################################################
#
# Remove files:
#

# Try to leave directory first:
chdir("$oldDir") || chdir() || chdir("/") ||  
    warn "$0 cannot leave directory `$workDir'.\n";
system ("rm -r $workDir");

=pod

=back

=back

Of course you are not limited to just setting some variables in your
configuration file; in fact you can do about anything you like, as
long as you have sufficient privileges. And this is where security
problems start surfacing:

=head2 Security issues

Any commands in the configuration file will be executed whenever
B<texprL3> is called, and they will be running with your UID and GID
(if your operating system supports those). On the other hand, people
rarely check configuration files once the system is up and running.
The combination of these two factors poses a potential threat to your
system's security, and you yourself must decide whether to take that
risc. The author of the program himself is by no means an expert in
this field, and fortunately he knows that. Here are some suggestions
he thinks might make using B<texprL3> safer:

=over 4

=item *

Never make your configuration file writable for anyone except trusted
users! Better still: always make sure that only the file's owner has
the right to change the file.

=item *

Never make the directory holding the configuration file writable for
anyone except trusted users, or your configuration file might be
erased or replaced by a different one.

=item *

Always be careful when you input another file via C<do()>, or you will
import another person's risks into your own account. Presumably you can
trust your system administrator to be careful, so that it might be considered
safe to input the system-wide configuration file F</etc/.texprL3rc>.

=item *

Specify full pathnames for binaries (variables C<$TeX> and
C<$printDvi>, options C<--tex> and C<--print>), do not rely on
B<texprL3>'s calling B<which>.

=back

=head1 EXAMPLES

The usual way to print a file is to say C<texprL3 E<lt> I<file>>; or
you can place the command at the end of a pipeline: C<...|texprL3>.

If you want to use B<texprL3> like a typewriter, you can simply call
C<texprL3> and send an EOF when you are done.

If you want to get the TeX-code setting your Latin-3 text I<text> for
inclusion in other TeX-documents and to store it in file I<dest.tex>,
simply call C<texprL3 --tex 'cat E<gt>> I<dest.tex>C<' --print 'sleep 1'
E<lt>> I<text>; if you do not include the C<--print> option, B<texprL3>
will C<die()> complaining about TeX not producing any output, leaving
the working directory for future annoyance.

If the program doesn't work, you should do C<texprL3 --inspect E<lt>
I<text>>; the program will tell you in which directory you will
find the F<texput.log> file.

If you want to get a list of all the characters and see how they are
printed, you could pipe the output of a short Perl script to
B<texprL3>: C<perl -e 'for ($i=0; $iE<lt>256; $i++) {print "$i:"
. chr($i) . "\n"}' | texprL3>.

If you do not want to read any configuration file, call C<texprL3
--config /dev/null E<lt> I<text>>.

=head1 ENVIRONMENT

There is only one environment variable that is used,
I<TEXPRLATIN3_RC>. See the explanations for command line option
C<--config>.

=head1 FILES

=over 4

=item F<~/.texprL3rc>

User-specific configuration file.

=item F</etc/texprL3rc>

System-wide configuration file.

=back

=head1 NOTES

B<texprL3> is meant to be used with languages that are not supported
by other popular tools, like B<a2ps>; specifically, it grew out of the
author's problems of printing texts in Esperanto without much effort.
So it is rather awkward that the only documentation should be in
English, which does not have to face the problems B<texprl3>
addresses. Translations into other languages are welcome, as are hints
on how to get as many characters of the character set right I<without
giving up full portability and reliance only on plain TeX> (this rules
out any solutions involving non-standard fonts; also there is probably
little use in building up these characters from many C<\vrule>s.) If,
however, you have found such a non-portable solution for a particular
language that works for you, and you want to share it with others,
these indications are also welcome and might be included in the
B<Examples> section or in a (possibly language-specific) F<README>
file to accompany the script, even though they will I<not> become part
of the script itself.

Latest information on and current versions of B<texprL3> will be
obtainable from
F<http://www.geocities.com/ResearchTriangle/Node/7900/texprL3.html>.

=head1 BUGS

Does not yet handle all characters in the set (but see above for
reason).

There seems to be some alignment problem, but maybe it is not
texprL3's fault.

=head1 AUTHOR

Albert Reiner (forgesu@geocities.com).

=cut
