% \iffalse meta-comment % % Copyright (C) 2003-2024 Scott Pakin % -------------------------------------------------------- % % This file may be distributed and/or modified under the conditions of % the LaTeX Project Public License, either version 1.3c of this license % or (at your option) any later version. The latest version of this % license is in: % % http://www.latex-project.org/lppl.txt % % and version 1.3c or later is part of all distributions of LaTeX % version 2006/05/20 or later. % % \fi % % \iffalse %<*driver> \ProvidesFile{perltex.dtx} % %\NeedsTeXFormat{LaTeX2e}[1999/12/01] %\ProvidesPackage{perltex} %<*package> [2024/12/03 v2.3 LaTeX macros for use with PerlTeX] % % %<*driver> \documentclass{ltxdoc} \makeatletter \def\plmac@tag{FakingBeingRunFromPerlTeX} \def\plmac@tofile{\jobname.topl} \def\plmac@fromfile{\jobname.frpl} \def\plmac@toflag{\jobname.tfpl} \def\plmac@fromflag{\jobname.ffpl} \def\plmac@doneflag{\jobname.dfpl} \def\plmac@pipe{\jobname.pipe} \makeatother \usepackage{perltex} \usepackage{varioref} \usepackage{flafter} \usepackage{textcomp} \usepackage{graphicx} \usepackage{hyperref} \hypersetup{% hyperindex=false, bookmarksopen, pdftitle={PerlTeX: Defining LaTeX macros in terms of Perl code}, pdfauthor={Scott Pakin, scott+pt@pakin.org}, pdfsubject={Using Perl to define LaTeX macros}, pdfkeywords={programming, LaTeX, macros, Perl} } \EnableCrossrefs \CodelineIndex \RecordChanges \setcounter{IndexColumns}{2} \begin{document} \DocInput{perltex.dtx} \end{document} % % \fi % % \CheckSum{622} % % \CharacterTable % {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z % Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z % Digits \0\1\2\3\4\5\6\7\8\9 % Exclamation \! Double quote \" Hash (number) \# % Dollar \$ Percent \% Ampersand \& % Acute accent \' Left paren \( Right paren \) % Asterisk \* Plus \+ Comma \, % Minus \- Point \. Solidus \/ % Colon \: Semicolon \; Less than \< % Equals \= Greater than \> Question mark \? % Commercial at \@ Left bracket \[ Backslash \\ % Right bracket \] Circumflex \^ Underscore \_ % Grave accent \` Left brace \{ Vertical bar \| % Right brace \} Tilde \~} % % % \changes{v1.0}{2003/07/13}{Initial version} % \changes{v1.2}{2004/10/07}{Renamed \texttt{perlmacros.sty} to % \texttt{perltex.sty} for consistency.} % % \GetFileInfo{perltex.sty} % % \DoNotIndex{\$, \', \(, \), \., \\, \{, \}, \^, \", \@} % \DoNotIndex{\@empty, \@ifnextchar, \@ifundefined, \@makeother, \@ne} % \DoNotIndex{\@permittedops} % \DoNotIndex{\A, \active, \advance} % \DoNotIndex{\b, \begin, \begingroup, \bgroup, \btrapped} % \DoNotIndex{\catcode, \csname} % \DoNotIndex{\d, \def, \documentclass} % \DoNotIndex{\E, \edef, \egroup, \else, \end, \endcsname, \endgroup} % \DoNotIndex{\expandafter} % \DoNotIndex{\fi} % \DoNotIndex{\gdef, \global} % \DoNotIndex{\ifeof, \ifnum, \ifx, \immediate} % \DoNotIndex{\let, \loop} % \DoNotIndex{\makeatletter, \makeatother, \MessageBreak} % \DoNotIndex{\n, \newcommand, \newcount, \newenvironment, \newif} % \DoNotIndex{\newread, \newtoks, \newwrite} % \DoNotIndex{\noexpand, \number} % \DoNotIndex{\obeyspaces} % \DoNotIndex{\ProcessOptions, \protect} % \DoNotIndex{\Q} % \DoNotIndex{\r, \relax, \repeat} % \DoNotIndex{\s, \space, \string} % \DoNotIndex{\TeX, \the} % \DoNotIndex{\W} % % \newcommand{\indexpkg}[1]{^^A ^^A Index a package name % \index{#1=\textsf{#1} (package)}^^A % \index{packages>#1=\textsf{#1}}^^A % } % \newcommand{\pkgname}[1]{^^A ^^A Name of an arbitrary package % \textsf{#1}^^A % \indexpkg{#1}^^A % } % \newcommand{\PerlTeX}{Perl\TeX{}} ^^A Name of the complete system % \newcommand{\perltex}{\texttt{perltex.pl}} ^^A Name of the Perl script % \newcommand{\perlmac}{^^A ^^A Name of the LaTeX package % \texttt{perltex.sty}^^A % \indexpkg{perltex}^^A % } % \newcommand{\noperlmac}{\texttt{noperltex.sty}} ^^A Name of the alternate LaTeX package % \newcommand{\XeTeX}{^^A ^^A Name of the typesetting system % X\lower0.5ex\hbox{\kern-0.15em\reflectbox{E}}\kern-0.1667em\TeX^^A % } % \newcommand{\signal}[1]{^^A ^^A Signal handler % \lowercase{\textsc{sig#1}}^^A % \index{SIG#1=\lowercase{\textsc{sig#1}}}^^A % } % % ^^A Typeset package options. % \newcommand{\DescribeOption}[1]{^^A % \marginpar{\raggedleft\PrintDescribeEnv{#1}}} % \newcommand{\pkgoption}[1]{^^A % \texttt{#1}^^A % \index{#1=\texttt{#1} (package option)}^^A % \index{package options>#1=\texttt{#1}}^^A % } % % % ^^A Define an environment just like macro but for Perl subroutines % \makeatletter % \newenvironment{perlsub}{^^A % \begingroup % \catcode`\_=11 % \def\PrintMacroName##1{\strut\MacroFont\string##1\ }^^A % \def\SpecialIndex@##1##2{^^A % \@bsphack % \special@index{\string##1\actualchar % \string\verb\quotechar*\verbatimchar##1\verbatimchar##2}^^A % \@esphack % }^^A % \begin{macro}^^A % }{^^A % \end{macro}^^A % \endgroup % } % \makeatother % % ^^A Define an environment just like macro but for Perl scalars or lists. % \makeatletter % \newenvironment{perlvar}{^^A % \begingroup % \catcode`\_=11 % \def\PrintMacroName##1{\strut\MacroFont\string##1\ }^^A % \def\SpecialIndex@##1##2{^^A % \@bsphack % \special@index{\expandafter\@gobble\string##1\actualchar % \string\verb\quotechar*\verbatimchar\string##1\verbatimchar##2}^^A % \@esphack % }^^A % \begin{macro}^^A % }{^^A % \end{macro}^^A % \endgroup % } % \makeatother % % \title{\PerlTeX: \\ Defining \LaTeX{} macros in terms of Perl code^^A % \thanks{This document corresponds to \PerlTeX~\fileversion, dated % \filedate.}} % \author{Scott Pakin \\ \texttt{scott+pt@pakin.org}} % % \maketitle % \sloppy % % \begin{abstract} % \PerlTeX{} is a combination Perl script (\perltex) and \LaTeXe{} % style file (\perlmac) that, together, give the user the ability to % define \LaTeX{} macros in terms of Perl code. Once defined, a Perl % macro becomes indistinguishable from any other \LaTeX{} macro. % \PerlTeX{} thereby combines \LaTeX's typesetting power with Perl's % programmability. % \end{abstract} % % \section{Introduction} % % \TeX{} is a professional-quality typesetting system. However, its % programming language is rather hard to use for anything but the most % simple forms of text substitution. Even \LaTeX, the most popular % macro package for \TeX, does little to simplify \TeX{} programming. % % Perl is a general-purpose programming language whose forte is in text % manipulation. However, it has no support whatsoever for typesetting. % % \PerlTeX's goal is to bridge these two worlds. It enables the % construction of documents that are primarily \LaTeX-based but contain % a modicum of Perl. \PerlTeX{} seamlessly integrates Perl code into a % \LaTeX{} document, enabling the user to define macros whose bodies % consist of Perl code instead of \TeX{} and \LaTeX{} code. % % As an example, suppose you need to define a macro that reverses a set % of words. Although it sounds like it should be simple, few \LaTeX{} % authors are sufficiently versed in the \TeX{} language to be able to % express such a macro. However, a word-reversal function is easy to % express in Perl: one need only |split| a string into a list of words, % |reverse| the list, and |join| it back together. The following is how % a |\reversewords| macro could be defined using \PerlTeX: % % \begin{verbatim} % \perlnewcommand{\reversewords}[1]{join " ", reverse split " ", $_[0]} % \end{verbatim} % % \noindent % Then, executing ``\texttt{\string\reversewords\string{Try doing this % without Perl!\string}}'' in a document would produce the text ``Perl! % without this doing Try''. Simple, isn't it? % % As another example, think about how you'd write a macro in \LaTeX{} to % extract a substring of a given string when provided with a starting % position and a length. Perl has an built-in |substr| function and % \PerlTeX{} makes it easy to export this to \LaTeX: % % \begin{verbatim} % \perlnewcommand{\substr}[3]{substr $_[0], $_[1], $_[2]} % \end{verbatim} % % |\substr| can then be used just like any other \LaTeX{} macro---and as % simply as Perl's |substr| function: % % \bigskip % % \noindent| |^^A % \begin{tabular}{@{}l@{}} % |\newcommand{\str}{superlative}| \\ % |A sample substring of ``\str'' is ``\substr{\str}{2}{4}''.| \\[1ex] % \multicolumn{1}{@{}c@{}}{\Huge$\Downarrow$} \\[2ex] % \multicolumn{1}{@{}c@{}}{A sample substring of ``superlative'' is ``perl''.} % \end{tabular} % % \bigskip % % To present a somewhat more complex example, observe how much % easier it is to generate a repetitive matrix using Perl code than % ordinary \LaTeX{} commands: % % \begin{verbatim} % \perlnewcommand{\hilbertmatrix}[1]{ % my $result = ' % \[ % \renewcommand{\arraystretch}{1.3} % '; % $result .= '\begin{array}{' . 'c' x $_[0] . "}\n"; % foreach $j (0 .. $_[0]-1) { % my @row; % foreach $i (0 .. $_[0]-1) { % push @row, ($i+$j) ? (sprintf '\frac{1}{%d}', $i+$j+1) : '1'; % } % $result .= join (' & ', @row) . " \\\\\n"; % } % $result .= '\end{array} % \]'; % return $result; % } % % \hilbertmatrix{20} % \end{verbatim} % % \begin{center} % {\Huge$\Downarrow$} % \end{center} % % \[ % \renewcommand{\arraystretch}{1.3} % \begin{array}{ccccccccccccccc} % 1 & \frac{1}{2} & \frac{1}{3} & \frac{1}{4} & \frac{1}{5} & \frac{1}{6} & \frac{1}{7} & \frac{1}{8} & \frac{1}{9} & \frac{1}{10} & \frac{1}{11} & \frac{1}{12} & \frac{1}{13} & \frac{1}{14} & \frac{1}{15} \\ % \frac{1}{2} & \frac{1}{3} & \frac{1}{4} & \frac{1}{5} & \frac{1}{6} & \frac{1}{7} & \frac{1}{8} & \frac{1}{9} & \frac{1}{10} & \frac{1}{11} & \frac{1}{12} & \frac{1}{13} & \frac{1}{14} & \frac{1}{15} & \frac{1}{16} \\ % \frac{1}{3} & \frac{1}{4} & \frac{1}{5} & \frac{1}{6} & \frac{1}{7} & \frac{1}{8} & \frac{1}{9} & \frac{1}{10} & \frac{1}{11} & \frac{1}{12} & \frac{1}{13} & \frac{1}{14} & \frac{1}{15} & \frac{1}{16} & \frac{1}{17} \\ % \frac{1}{4} & \frac{1}{5} & \frac{1}{6} & \frac{1}{7} & \frac{1}{8} & \frac{1}{9} & \frac{1}{10} & \frac{1}{11} & \frac{1}{12} & \frac{1}{13} & \frac{1}{14} & \frac{1}{15} & \frac{1}{16} & \frac{1}{17} & \frac{1}{18} \\ % \frac{1}{5} & \frac{1}{6} & \frac{1}{7} & \frac{1}{8} & \frac{1}{9} & \frac{1}{10} & \frac{1}{11} & \frac{1}{12} & \frac{1}{13} & \frac{1}{14} & \frac{1}{15} & \frac{1}{16} & \frac{1}{17} & \frac{1}{18} & \frac{1}{19} \\ % \frac{1}{6} & \frac{1}{7} & \frac{1}{8} & \frac{1}{9} & \frac{1}{10} & \frac{1}{11} & \frac{1}{12} & \frac{1}{13} & \frac{1}{14} & \frac{1}{15} & \frac{1}{16} & \frac{1}{17} & \frac{1}{18} & \frac{1}{19} & \frac{1}{20} \\ % \frac{1}{7} & \frac{1}{8} & \frac{1}{9} & \frac{1}{10} & \frac{1}{11} & \frac{1}{12} & \frac{1}{13} & \frac{1}{14} & \frac{1}{15} & \frac{1}{16} & \frac{1}{17} & \frac{1}{18} & \frac{1}{19} & \frac{1}{20} & \frac{1}{21} \\ % \frac{1}{8} & \frac{1}{9} & \frac{1}{10} & \frac{1}{11} & \frac{1}{12} & \frac{1}{13} & \frac{1}{14} & \frac{1}{15} & \frac{1}{16} & \frac{1}{17} & \frac{1}{18} & \frac{1}{19} & \frac{1}{20} & \frac{1}{21} & \frac{1}{22} \\ % \frac{1}{9} & \frac{1}{10} & \frac{1}{11} & \frac{1}{12} & \frac{1}{13} & \frac{1}{14} & \frac{1}{15} & \frac{1}{16} & \frac{1}{17} & \frac{1}{18} & \frac{1}{19} & \frac{1}{20} & \frac{1}{21} & \frac{1}{22} & \frac{1}{23} \\ % \frac{1}{10} & \frac{1}{11} & \frac{1}{12} & \frac{1}{13} & \frac{1}{14} & \frac{1}{15} & \frac{1}{16} & \frac{1}{17} & \frac{1}{18} & \frac{1}{19} & \frac{1}{20} & \frac{1}{21} & \frac{1}{22} & \frac{1}{23} & \frac{1}{24} \\ % \frac{1}{11} & \frac{1}{12} & \frac{1}{13} & \frac{1}{14} & \frac{1}{15} & \frac{1}{16} & \frac{1}{17} & \frac{1}{18} & \frac{1}{19} & \frac{1}{20} & \frac{1}{21} & \frac{1}{22} & \frac{1}{23} & \frac{1}{24} & \frac{1}{25} \\ % \frac{1}{12} & \frac{1}{13} & \frac{1}{14} & \frac{1}{15} & \frac{1}{16} & \frac{1}{17} & \frac{1}{18} & \frac{1}{19} & \frac{1}{20} & \frac{1}{21} & \frac{1}{22} & \frac{1}{23} & \frac{1}{24} & \frac{1}{25} & \frac{1}{26} \\ % \frac{1}{13} & \frac{1}{14} & \frac{1}{15} & \frac{1}{16} & \frac{1}{17} & \frac{1}{18} & \frac{1}{19} & \frac{1}{20} & \frac{1}{21} & \frac{1}{22} & \frac{1}{23} & \frac{1}{24} & \frac{1}{25} & \frac{1}{26} & \frac{1}{27} \\ % \frac{1}{14} & \frac{1}{15} & \frac{1}{16} & \frac{1}{17} & \frac{1}{18} & \frac{1}{19} & \frac{1}{20} & \frac{1}{21} & \frac{1}{22} & \frac{1}{23} & \frac{1}{24} & \frac{1}{25} & \frac{1}{26} & \frac{1}{27} & \frac{1}{28} \\ % \frac{1}{15} & \frac{1}{16} & \frac{1}{17} & \frac{1}{18} & \frac{1}{19} & \frac{1}{20} & \frac{1}{21} & \frac{1}{22} & \frac{1}{23} & \frac{1}{24} & \frac{1}{25} & \frac{1}{26} & \frac{1}{27} & \frac{1}{28} & \frac{1}{29} \\ % \end{array} % \] % % \bigskip % % In addition to |\perlnewcommand| and |\perlrenewcommand|, \PerlTeX{} % supports |\perlnewenvironment| and |\perlrenewenvironment| macros. % These enable environments to be defined using Perl code. The % following example, a |spreadsheet| environment, generates a |tabular| % environment plus a predefined header row. This example would have % been much more difficult to implement without \PerlTeX: % % \begin{verbatim} % \newcounter{ssrow} % \perlnewenvironment{spreadsheet}[1]{ % my $cols = $_[0]; % my $header = "A"; % my $tabular = "\\setcounter{ssrow}{1}\n"; % $tabular .= '\newcommand*{\rownum}{\thessrow\addtocounter{ssrow}{1}}' . "\n"; % $tabular .= '\begin{tabular}{@{}r|*{' . $cols . '}{r}@{}}' . "\n"; % $tabular .= '\\multicolumn{1}{@{}c}{} &' . "\n"; % foreach (1 .. $cols) { % $tabular .= "\\multicolumn{1}{c"; % $tabular .= '@{}' if $_ == $cols; % $tabular .= "}{" . $header++ . "}"; % if ($_ == $cols) { % $tabular .= " \\\\ \\cline{2-" . ($cols+1) . "}" % } % else { % $tabular .= " &"; % } % $tabular .= "\n"; % } % return $tabular; % }{ % return "\\end{tabular}\n"; % } % % \begin{center} % \begin{spreadsheet}{4} % \rownum & 1 & 8 & 10 & 15 \\ % \rownum & 12 & 13 & 3 & 6 \\ % \rownum & 7 & 2 & 16 & 9 \\ % \rownum & 14 & 11 & 5 & 4 % \end{spreadsheet} % \end{center} % \end{verbatim} % % \begin{center} % {\Huge$\Downarrow$} % \end{center} % % \DeleteShortVerb{\|} % \begin{center} % \begin{tabular}{@{}r|*{4}{r}@{}} % \multicolumn{1}{@{}c}{} & % \multicolumn{1}{c}{A} & % \multicolumn{1}{c}{B} & % \multicolumn{1}{c}{C} & % \multicolumn{1}{c@{}}{D} \\ \cline{2-5} % 1 & 1 & 8 & 10 & 15 \\ % 2 & 12 & 13 & 3 & 6 \\ % 3 & 7 & 2 & 16 & 9 \\ % 4 & 14 & 11 & 5 & 4 % \end{tabular} % \end{center} % \MakeShortVerb{\|} % % % \section{Usage} % % There are two components to using \PerlTeX\@. First, documents must % include a ``|\usepackage{perltex}|'' line in their preamble in % order to define |\perlnewcommand|, |\perlrenewcommand|, % |\perlnewenvironment|, and |\perlrenewenvironment|. Second, \LaTeX{} % documents must be compiled using the \perltex{} wrapper script. % % \subsection{Defining and redefining Perl macros} % % \DescribeMacro{\perlnewcommand} % \DescribeMacro{\perlrenewcommand} % \DescribeMacro{\perlnewenvironment} % \DescribeMacro{\perlrenewenvironment} % \DescribeMacro{\perldo} % \perlmac{} defines five macros: |\perlnewcommand|, % |\perlrenewcommand|, |\perlnewenvironment|, |\perlrenewenvironment|, % and |\perldo|. The first four of these behave exactly like their % \LaTeXe{} counterparts---|\newcommand|, |\renewcommand|, % |\newenvironment|, and |\renewenvironment|---except that the macro % body consists of Perl code that dynamically generates \LaTeX{} code. % \perlmac{} even includes support for optional arguments and the % starred forms of its commands (i.e.~|\perlnewcommand*|, % |\perlrenewcommand*|, |\perlnewenvironment*|, and % |\perlrenewenvironment*|). |\perldo| immediately executes a block of % Perl code without (re)defining any macros or environments. % % A \PerlTeX-defined macro or environments is converted to a Perl % subroutine named after the macro/environment but beginning with % ``|latex_|''. For example, a \PerlTeX-defined \LaTeX{} macro called % |\myMacro| internally produces a Perl subroutine called % |latex_myMacro|. Macro arguments are converted to subroutine % arguments. A \LaTeX{} macro's |#1| argument is referred to as |$_[0]| % in Perl; |#2| is referred to as |$_[1]|; and so forth. % % Any valid Perl code can be used in the body of a macro. However, % \PerlTeX{} executes the Perl code within a secure sandbox. This means % that potentially harmful Perl operations, such as |unlink|, |rmdir|, % and |system| will result in a run-time error. (It is possible to % disable the safety checks, however, as is explained in % Section~\ref{sec:perltex-man}.) Having a secure sandbox implies that % it is safe to build \PerlTeX{} documents written by other people % without worrying about what they may do to your computer system. % % A single sandbox is used for the entire |latex| run. This means that % multiple macros defined by |\perlnewcommand| can invoke each other. % It also means that global variables persist across macro calls: % % \bigskip % % \noindent| |^^A % \begin{tabular}{@{}l@{}} % |\perlnewcommand{\setX}[1]{$x = $_[0]; return ""}| \\ % |\perlnewcommand{\getX}{'$x$ was set to ' . $x . '.'}| \\ % |\setX{123}| \\ % |\getX| \\ % |\setX{456}| \\ % |\getX| \\ % |\perldo{$x = 789}| \\ % |\getX| \\[1ex] % \multicolumn{1}{@{}c@{}}{\Huge$\Downarrow$} \\[2ex] % \multicolumn{1}{@{}c@{}}{^^A % $x$ was set to 123. $x$ was set to 456. $x$ was set to 789.^^A % } \\ % \end{tabular} % % \bigskip % % Macro arguments are expanded by \LaTeX{} before being passed to Perl. % Consider the following macro definition, which wraps its argument % within |\begin{verbatim*}|\dots\linebreak[0]|\end{verbatim*}|: % % \begin{verbatim} % \perlnewcommand{\verbit}[1]{ % "\\begin{verbatim*}\n$_[0]\n\\end{verbatim*}\n" % } % \end{verbatim} % % \noindent % An invocation of % ``|\verbit{\TeX}|'' would therefore typeset the \emph{expansion} of % ``|\TeX|'', namely ``|T\kern| |-.1667em\lower| |.5ex\hbox| |{E}\kern| % |-.125emX\spacefactor| |\@m|'', which might be a bit unexpected. The % solution is to use |\noexpand|: % |\verbit{\noexpand\TeX}|~$\Rightarrow$ |\TeX|\@. ``Robust'' macros as % well as |\begin| and |\end| are implicitly preceded by |\noexpand|. % % % \subsection{Making \perltex\ optional} % \label{sec:perltex-opt} % % Normally, \perlmac\ issues a \texttt{Document must be compiled using % perltex} error if a document specifies |\usepackage{perltex}| but is % not compiled using \perltex. However, sometimes \PerlTeX\ may be % needed merely to enhance a document's formatting without being % mandatory for compiling the document. For such cases, the % \DescribeOption{optional} % \pkgoption{optional} package option instructs \perlmac\ only to note % that \texttt{Document was compiled without using the perltex script} % without aborting the compilation. % \DescribeMacro{\ifperl} % The author can then use the |\ifperl| macro to test if \perltex\ is % being used and, if not, provide alternative definitions for macros and % environments defined with |\perlnewcommand| and |\perlnewenvironment|. % % See Section~\ref{sec:complete-example} for a large \PerlTeX\ example % that uses \pkgoption{optional} and |\ifperl| to define an environment % one way if \perltex\ is detected and another way if not. The text % preceding the example also shows how to enable a document to compile % even if \perlmac\ is not even installed. % % % \subsection{Invoking \perltex} % \label{sec:perltex-man} % % The following pages reproduce the \perltex{} program documentation. % Key parts of the documentation are excerpted when \perltex{} is % invoked with the |--help| option. The various Perl % |pod2|\meta{something} tools can be used to generate the complete % program documentation in a variety of formats such as \LaTeX, HTML, % plain text, or Unix man-page format. For example, the following % command is the recommended way to produce a Unix man page from % \perltex: % % \enlargethispage{4ex} % \begin{verbatim} % pod2man --center=" " --release=" " perltex.pl > perltex.1 % \end{verbatim} % % \clearpage % \begingroup % \def\index#1{} % \def\section#1{} % \let\subsection=\subsubsection % % ^^A *************************************************************** % ^^A * The following was generated using pod2latex and touched up * % ^^A * slightly to make it better match the rest of this document. * % ^^A *************************************************************** % % \def\C++{{\rm C\kern-.05em\raise.3ex\hbox{\footnotesize ++}}} % \def\underscore{\leavevmode\kern.04em\vbox{\hrule width 0.4em height 0.3pt}} % \setlength{\parindent}{0pt} % % \section{PERLTEX.PL}% % \index{PERLTEX.PL} % % \subsection*{NAME} % perltex --- enable \LaTeX{} macros to be defined in terms of Perl code % % \subsection*{SYNOPSIS} % perltex % [\textbf{--help}] % [\textbf{--latex}=\textit{program}] % [\textbf{--}[\textbf{no}]\textbf{safe}] % [\textbf{--permit}=\textit{feature}] % [\textbf{--makesty}] % [\textit{latex options}] % % \subsection*{DESCRIPTION} % \LaTeX---through the underlying \TeX{} typesetting system---produces % beautifully typeset documents but has a macro language that is % difficult to program. In particular, support for complex string % manipulation is largely lacking. Perl is a popular general-purpose % programming language whose forte is string manipulation. However, it % has no typesetting capabilities whatsoever. % % \bigskip % % Clearly, Perl's programmability could complement \LaTeX's typesetting % strengths. \textbf{perltex} is the tool that enables a symbiosis % between the two systems. All a user needs to do is compile a \LaTeX\ % document using \textbf{perltex} instead of \textbf{latex}. % (\textbf{perltex} is actually a wrapper for \textbf{latex}, so no % \textbf{latex} functionality is lost.) If the document includes a % \texttt{\string\usepackage\{perltex\}} in its preamble, then % \texttt{\string\perlnewcommand} and \texttt{\string\perlrenewcommand} % macros will be made available. These behave just like \LaTeX's % \texttt{\string\newcommand} and \texttt{\string\renewcommand} except % that the macro body contains Perl code instead of \LaTeX\ code. % % \subsection*{OPTIONS}% % \index{OPTIONS} % % {\bf perltex} accepts the following command-line options: % % \begin{description} % % % \item[{\textbf{-{}-help}}] \mbox{} % % Display basic usage information. % % % \item[{\textbf{-{}-latex}=\textit{program}}] \mbox{} % % Specify a program to use instead of \textbf{latex}. For example, % \texttt{--latex=pdflatex} would typeset the given document using % \textbf{pdflatex} instead of ordinary \textbf{latex}. % % % \item[{\textbf{-{}-}[\textbf{no}]\textbf{safe}}] \mbox{} % % Enable or disable sandboxing. With the default of \textbf{-{}-safe}, % \textbf{perltex} executes the code from a % \texttt{\string\perlnewcommand} or \texttt{\string\perlrenewcommand} % macro within a protected environment that prohibits ``unsafe'' % operations such as accessing files or executing external programs. % Specifying \textbf{-{}-nosafe} gives the \LaTeX\ document \textit{carte % blanche} to execute any arbitrary Perl code, including that which can % harm the user's files. See \emph{Safe} for more information. % % \item[{\textbf{-{}-permit}=\textit{feature}}] \mbox{} % % Permit particular Perl operations to be performed. The % \textbf{-{}-permit} option, which can be specified more than once on % the command line, enables finer-grained control over the % \textbf{perltex} sandbox. See \emph{Opcode} for more information. % % % \item[{\textbf{-{}-makesty}}] \mbox{} % % Generate a \LaTeX\ style file called \emph{noperltex.sty}. Replacing % the document's \texttt{\string\usepackage\{perltex\}} line with % \texttt{\string\usepackage\{noperltex\}} produces the same output % but does not require \PerlTeX, making the document suitable for % distribution to people who do not have \PerlTeX\ installed. The % disadvantage is that \emph{noperltex.sty} is specific to the document % that produced it. Any changes to the document's \PerlTeX\ macro % definitions or macro invocations necessitates rerunning % \textbf{perltex} with the \textbf{-{}-makesty} option. % % \end{description} % % These options are then followed by whatever options are normally % passed to \textbf{latex} (or whatever program was specified with % \texttt{--latex}), including, for instance, the name of the % \emph{.tex} file to compile. % % \subsection*{EXAMPLES} % % In its simplest form, \textbf{perltex} is run just like \textbf{latex}: % % \begin{verbatim} % perltex myfile.tex % \end{verbatim} % % % To use \textbf{pdflatex} instead of regular \textbf{latex}, use the % \textbf{-{}-latex} option: % % \begin{verbatim} % perltex --latex=pdflatex myfile.tex % \end{verbatim} % % % If \LaTeX\ gives a ``\texttt{trapped by operation mask}'' error and % you trust the \emph{.tex} file you're trying to compile not to execute % malicious Perl code (e.g., because you wrote it yourself), you can % disable \textbf{perltex}'s safety mechansisms with % \textbf{-{}-nosafe}: % % \begin{verbatim} % perltex --nosafe myfile.tex % \end{verbatim} % % % The following command gives documents only \textbf{perltex}'s default % permissions (\texttt{:browse}) plus the ability to open files and invoke the % \texttt{time} command: % % \begin{verbatim} % perltex --permit=:browse --permit=:filesys_open % --permit=time myfile.tex % \end{verbatim} % % % \subsection*{ENVIRONMENT} % % \textbf{perltex} honors the following environment variables: % % \begin{description} % % \item[{PERLTEX}] \mbox{} % % Specify the filename of the \LaTeX\ compiler. The \LaTeX\ compiler % defaults to ``\texttt{latex}''. The \texttt{PERLTEX} environment % variable overrides this default, and the \textbf{-{}-latex} % command-line option (see \textsf{OPTIONS}) overrides that. % % \end{description} % % % \subsection*{FILES} % % While compiling \emph{jobname.tex}, \textbf{perltex} makes use of the % following files: % % \begin{description} % % \item[{\emph{jobname.lgpl}}] \mbox{} % % log file written by Perl; helpful for debugging Perl macros % % % \item[{\emph{jobname.topl}}] \mbox{} % % information sent from \LaTeX\ to Perl % % % \item[{\emph{jobname.frpl}}] \mbox{} % % information sent from Perl to \LaTeX\ % % % \item[{\emph{jobname.tfpl}}] \mbox{} % % ``flag'' file whose existence indicates that \emph{jobname.topl} contains % valid data % % % \item[{\emph{jobname.ffpl}}] \mbox{} % % ``flag'' file whose existence indicates that \emph{jobname.frpl} contains % valid data % % % \item[{\emph{jobname.dfpl}}] \mbox{} % % ``flag'' file whose existence indicates that \emph{jobname.ffpl} has been % deleted % % % \item[{\emph{noperltex-\#.tex}}] \mbox{} % % file generated by \emph{noperltex.sty} for each \PerlTeX\ macro invocation % % \end{description} % % % \subsection*{NOTES} % % \textbf{perltex}'s sandbox defaults to what \emph{Opcode} calls % ``\texttt{:browse}''. % % % \subsection*{SEE ALSO} % % latex(1), pdflatex(1), perl(1), Safe(3pm), Opcode(3pm) % % % \subsection*{AUTHOR} % % Scott Pakin, \textit{scott+pt@pakin.org} % % % ^^A *********************************** % ^^A * End of generated pod2latex text * % ^^A *********************************** % \endgroup % \clearpage % % % \subsection{A large, complete example} % \label{sec:complete-example} % % Suppose we want to define a |linkwords| environment that exhibits the % following characteristics: % % \begin{enumerate} % \item All words that appear within the environment's body are % automatically hyperlinked to a given URL that incorporates the % lowercase version of the word somewhere within that URL\@. % % \item The environment accepts an optional list of stop~words that % should not be hyperlinked. % % \item Paragraph breaks, nested environments, and other % \LaTeX\ markup are allowed within the environment's body. % \end{enumerate} % % Because of the reliance on text manipulation (parsing the % environment's body into words, comparing each word against the list of % stop~words, distinguishing between text and \LaTeX\ markup, etc.), % these requirements would be difficult to meet without \PerlTeX\@. % % We use three packages to help define the |linkwords| environment: % \pkgname{perltex} for text manipulation, \pkgname{hyperref} for % creating hyperlinks, and \pkgname{environ} for gathering up the body % of an environment and passing it as an argument to a macro. Most of % the work is performed by the \PerlTeX\ macro |\dolinkwords|, which % takes three arguments: a URL template that contains ``|\%s|'' as a % placeholder for a word from the text, a mandatory but possibly empty % space-separated list of lowercase stop~words, and the input text to % process. |\dolinkwords| first replaces all sequences of the form % |\|\meta{letters}, |\begin{|\meta{letters}|}|, or % |\end{|\meta{letters}|}| with dummy alphanumerics but remembers which % dummy sequence corresponds with each original \LaTeX\ sequence. The % macro then iterates over each word in the input text, formatting each % non-stop-word using the URL template. Contractions (words containing % apostrophes) are ignored. Finally, |\dolinkwords| replaces the dummy % sequences with the corresponding \LaTeX\ text and returns the result. % % The |linkwords| environment itself is defined using the |\NewEnviron| % macro from the \pkgname{environ} package. With |\NewEnviron|'s help, % |linkwords| accumulates its body into a |\BODY| macro and passes that % plus the URL template and the optional list of stop~words to % |\dolinkwords|. % % As an added bonus, % |\ifperl|\dots\linebreak[0]|\else|\dots\linebreak[0]|\fi| is used to % surround the definition of the |\dolinkwords| macro and |linkwords| % environment. If the document is not run through \perltex, |linkwords| % is defined as a do-nothing environment that simply typesets its body % as~is. Note that \perlmac\ is loaded with the \pkgoption{optional} % option to indicate that the document can compile without \perltex. % However, the user still needs \perlmac\ to avoid getting a % \texttt{File `perltex.sty' not found} error from \LaTeX\@. To produce % a document that can compile even without \perlmac\ installed, replace % the |\usepackage[optional]{perltex}| line with the following % \LaTeX\ code: % % \begin{samepage} % \begin{verbatim} % \IfFileExists{perltex.sty} % {\usepackage[optional]{perltex}} % {\newif\ifperl} % \end{verbatim} % \end{samepage} % % A complete \LaTeX\ document is presented below. This document, which % includes the definition and a use of the |linkwords| environment, can be % extracted from the \PerlTeX\ source code into a file called % |example.tex| by running % % \begin{verbatim} % tex perltex.ins % \end{verbatim} % % In the following listing, line numbers are suffixed with ``X'' to % distinguish them from line numbers associated with \PerlTeX's source % code. % % \bigskip % % ^^A We want the doc package to number and index the lines in our % ^^A code listing, but we want to keep line numbers and index entries % ^^A distinct from those of the PerlTeX package code. The following % ^^A machinations temporarily append an "X" to line numbers in the % ^^A code listing and in the index. % \makeatletter % \let\theOldCodelineNo=\theCodelineNo % \def\theCodelineNo{\reset@font\scriptsize \arabic{CodelineNo}X} % \def\appendX#1{#1X} % \def\codeline@wrindex@ex#1{\if@filesw % \immediate\write\@indexfile % {\string\indexentry{#1\encapchar appendX}^^A % {\number\c@CodelineNo}}\fi} % \ifcodeline@index % \let\special@index=\codeline@wrindex@ex % \fi % \makeatother % \iffalse %<*example> % \fi % % \begin{macrocode} \documentclass{article} \usepackage[optional]{perltex} \usepackage{environ} \usepackage{hyperref} \ifperl \perlnewcommand{\dolinkwords}[3]{ # Preprocess our arguments. $url = $_[0]; $url =~ s/\\\%s/\%s/g; %stopwords = map {lc $_ => 1} split " ", $_[1]; $stopwords{""} = 1; $text = $_[2]; # Replace LaTeX code in the text with placeholders. $placeholder = "ABCxyz123"; %substs = (); $replace = sub {$substs{$placeholder} = $_[0]; $placeholder++}; $text =~ s/\\(begin|end)\s+\{[a-z]+\}/$replace->($&)/gse; $text =~ s/\\[a-z]+/$replace->($&)/gse; # Hyperlink each word that's not in the stop list. $newtext = ""; foreach $word (split /((?<=[-\A\s])[\'a-z]+\b)/i, $text) { $lcword = lc $word; if (defined $stopwords{$lcword} || $lcword =~ /[^a-z]/) { $newtext .= $word; } else { $newtext .= sprintf "\\href{$url}{%s}", $lcword, $word; } } # Restore original text from placeholders and return the new text. while (($tag, $orig) = each %substs) { $newtext =~ s/\Q$tag\E/$orig/gs; } return $newtext; } \NewEnviron{linkwords}[2][]{\dolinkwords{#2}{#1}{\BODY}}{} \else \newenvironment{linkwords}[2][]{}{} \fi \begin{document} \newcommand{\stopwords}{a an the of in am and or but i we me you us them} \begin{linkwords}[\stopwords]{http://www.google.com/search?q=define:\%s} \begin{verse} I'm very good at integral and differential calculus; \\ I know the scientific names of beings animalculous: \\ In short, in matters vegetable, animal, and mineral, \\ I am the very model of a modern Major-General. \end{verse} \end{linkwords} \end{document} % \end{macrocode} % % \iffalse % % \fi % \makeatletter % \ifcodeline@index % \let\special@index=\codeline@wrindex % \fi % \let\theCodelineNo=\theOldCodelineNo % \makeatother % % % \StopEventually{^^A % \section{License agreement} % \label{sec:license} % % Copyright \textcopyright{} 2003--2024 Scott Pakin \texttt{} % % \providecommand{\url}[1]{\texttt{##1}} % % \bigskip % \noindent % These files may be distributed and/or modified under the conditions of % the \LaTeX{} Project Public License, either version~1.3c of this % license or (at your option) any later version. The latest version of % this license is in \url{http://www.latex-project.org/lppl.txt} and % version~1.3c or later is part of all distributions of \LaTeX{} version % 2006/05/20 or later. % % % \section*{Acknowledgments} % \label{sec:acknowledgments} % % Thanks to Andrew Mertz for writing the first draft of the code that % produces the \PerlTeX-free \noperlmac\ style file and for testing the % final draft; to Andrei Alexandrescu for providing a few bug fixes; to % Nick Andrewes for identifying and helping diagnose a problem running % \PerlTeX\ with \XeTeX\ and to Jonathan Kew for suggesting a % workaround; to Linus K\"allberg for reporting and helping diagnose % some problems with running \PerlTeX\ on Windows; and to Ulrike Fischer % for reporting and helping correct a bug encountered when using % \noperlmac\ with newer versions of \LaTeX\@. Also, thanks to the many % people who have sent me fan mail or submitted bug reports, % documentation corrections, or feature requests. (The % \texttt{\string\perldo} macro and the \texttt{--makesty} option were % particularly popular requests.) % % \PrintChanges % \PrintIndex % } % % \section{Implementation} % \label{sec:implementation} % % Users interested only in \emph{using} \PerlTeX{} can skip % Section~\ref{sec:implementation}, which presents the complete % \PerlTeX{} source code. This section should be of interest primarily % to those who wish to extend \PerlTeX{} or modify it to use a language % other than Perl. % % Section~\ref{sec:implementation} is split into two main parts. % Section~\ref{sec:perlmacros} presents the source code for \perlmac, % the \LaTeX{} side of \PerlTeX, and Section~\ref{sec:perltex} presents % the source code for \perltex, the Perl side of \PerlTeX\@. % \makeatletter % \@ifundefined{perlmaclines}{}{^^A % \newcount\perltex@lines % \perltex@lines=\perltexlines % \advance\perltex@lines by -\perlmaclines % In toto, \PerlTeX{} consists of a relatively small amount of code. % \perlmac{} is only \perlmaclines{} lines of \LaTeX{} and \perltex{} % is only \the\perltex@lines{} lines of Perl. % } % \makeatother % \perltex{} is fairly straightforward Perl code and shouldn't be too % difficult to understand by anyone comfortable with Perl programming. % \perlmac, in contrast, contains a bit of \LaTeX{} trickery and is % probably impenetrable to anyone who hasn't already tried his hand at % \LaTeX{} programming. Fortunately for the reader, the code is % profusely commented so the aspiring \LaTeX{} guru may yet learn % something from it. % % After documenting the \perlmac{} and \perltex{} source code, a few % suggestions are provided for porting \PerlTeX{} to use a backend % language other than Perl (Section~\ref{sec:porting}). % % \subsection{\perlmac} % \label{sec:perlmacros} % % \iffalse %<*package> % \fi % % Although I've written a number of \LaTeX{} packages, \perlmac{} was % the most challenging to date. The key things I needed to learn how to % do include the following: % % \begin{enumerate} % \item storing brace-matched---but otherwise not valid \LaTeX---code % for later use % % \item iterating over a macro's arguments % \end{enumerate} % % Storing non-\LaTeX{} code in a variable involves beginning a group in % an argumentless macro, fiddling with category codes, using % |\afterassignment| to specify a continuation function, and storing the % subsequent brace-delimited tokens in the input stream into a token % register. The continuation function, which also takes no arguments, % ends the group begun in the first function and proceeds using the % correctly |\catcode|d token register. This technique appears in % |\plmac@haveargs| and |\plmac@havecode| and in a simpler form % (i.e.,~without the need for storing the argument) in % |\plmac@write@perl| and |\plmac@write@perl@i|. % % Iterating over a macro's arguments is hindered by \TeX's requirement % that ``|#|'' be followed by a number or another ``|#|''. The % technique I discovered (which is used by the Texinfo source code) is % first to |\let| a variable be |\relax|, thereby making it % unexpandable, then to define a macro that uses that variable followed % by a loop variable, and finally to expand the loop variable and |\let| % the |\relax|ed variable be ``|#|'' right before invoking the macro. % This technique appears in |\plmac@havecode|. % % I hope you find reading the \perlmac{} source code instructive. % Writing it certainly was. % % % \subsubsection{Package initialization} % \label{sec:package-init} % % \begin{macro}{\ifplmac@required} % \begin{macro}{\plmac@requiredtrue} % \begin{macro}{\plmac@requiredfalse} % The |optional| package option lets an author specify that the document % can be built successfully even without \PerlTeX\@. Typically, this % means that the document uses |\ifperl| to help define % reduced-functionality equivalents of any document-defined % \PerlTeX\ macros and environments. When |optional| is not specified, % \perlmac\ issues an error message if the document is compiled without % using \perltex. When |optional| is specified, \perlmac\ suppresses % the error message. % \changes{v1.8}{2009/03/26}{Introduced an \texttt{optional} package % option to suppress the ``must be compiled using perltex'' error % message} % \begin{macrocode} \newif\ifplmac@required \plmac@requiredtrue \DeclareOption{optional}{\plmac@requiredfalse} \ProcessOptions\relax % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \PerlTeX{} defines six macros that are used for communication between % Perl and \LaTeX\@. |\plmac@tag| is a string of characters that should % never occur within one of the user's macro names, macro arguments, or % macro bodies. \perltex{} therefore defines |\plmac@tag| as a long % string of random uppercase letters. |\plmac@tofile| is the name of a % file used for communication from \LaTeX{} to Perl. |\plmac@fromfile| % is the name of a file used for communication from Perl to \LaTeX. % |\plmac@toflag| signals that |\plmac@tofile| can be read safely. % |\plmac@fromflag| signals that |\plmac@fromfile| can be read safely. % |\plmac@doneflag| signals that |\plmac@fromflag| has been deleted. % Table~\ref{tbl:variables} lists all of these variables along with the % value assigned to each by \perltex. % % \begin{table}[htbp] % \centering % \caption{Variables used for communication between Perl and \LaTeX} % \label{tbl:variables} % \begin{tabular}{@{}lll@{}} % \hline % Variable & Purpose & \perltex{} assignment \\ % \hline % % |\plmac@tag| & |\plmac@tofile| field separator & % (20 random letters) \\ % % |\plmac@tofile| & \LaTeX{} $\rightarrow$ Perl communication & % |\jobname.topl| \\ % % |\plmac@fromfile| & Perl $\rightarrow$ \LaTeX{} communication & % |\jobname.frpl| \\ % % |\plmac@toflag| & |\plmac@tofile| synchronization & % |\jobname.tfpl| \\ % % |\plmac@fromflag| & |\plmac@fromfile| synchronization & % |\jobname.ffpl| \\ % % |\plmac@doneflag| & |\plmac@fromflag| synchronization & % |\jobname.dfpl| \\ % % \hline % \end{tabular} % \end{table} % % \begin{macro}{\ifperl} % \begin{macro}{\perltrue} % \begin{macro}{\perlfalse} % The following block of code checks the existence of each of the % variables listed in Table~\ref{tbl:variables} plus |\plmac@pipe|, a % Unix named pipe used for to improve performance. If any variable is % not defined, \perlmac{} gives an error message and---as we shall see % on page~\pageref{page:define-dummies}---defines dummy versions of % |\perl|[|re|]|newcommand| and |\perl|[|re|]|newenvironment|. % \changes{v2.2}{2019/09/14}{Let-bind \cs{plmac@tag} to \cs{relax} if % \cs{plmac@tag} is undefined. This corrects a problem when % \noexpand\texttt{noperltex} is used with newer versions of % \noexpand\LaTeX} % \begin{macrocode} \newif\ifperl \perltrue \@ifundefined{plmac@tag}{\perlfalse\let\plmac@tag=\relax}{} \@ifundefined{plmac@tofile}{\perlfalse}{} \@ifundefined{plmac@fromfile}{\perlfalse}{} \@ifundefined{plmac@toflag}{\perlfalse}{} \@ifundefined{plmac@fromflag}{\perlfalse}{} \@ifundefined{plmac@doneflag}{\perlfalse}{} \@ifundefined{plmac@pipe}{\perlfalse}{} \ifperl \else \ifplmac@required \PackageError{perltex}{Document must be compiled using perltex} {Instead of compiling your document directly with latex, you need to\MessageBreak use the perltex script. \space perltex sets up a variety of macros needed by\MessageBreak the perltex package as well as a listener process needed for\MessageBreak communication between LaTeX and Perl.} \else \bgroup \obeyspaces \typeout{perltex: Document was compiled without using the perltex script;} \typeout{ it may not print as desired.} \egroup \fi \fi % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \subsubsection{Defining Perl macros} % \label{sec:perl-mac} % % \PerlTeX{} defines five macros intended to be called by the author. % Section~\ref{sec:perl-mac} details the implementation of two of them: % |\perlnewcommand| and |\perlrenewcommand|. % (Section~\ref{sec:perl-env} details the implementation of the next % two, |\perlnewenvironment| and |\perlrenewenvironment|; and, % Section~\ref{sec:perl-run} details the implementation of the final % macro, |\perldo|.) The goal is for these two macros to behave % \emph{exactly} like |\newcommand| and |\renewcommand|, respectively, % except that the author macros they in turn define have Perl bodies % instead of \LaTeX{} bodies. % % The sequence of the operations defined in this section is as follows: % % \begin{enumerate} % \item The user invokes |\perl|[|re|]|newcommand|, which stores % |\|[|re|]|newcommand| in |\plmac@command|. The % |\perl|[|re|]|newcommand| macro then invokes |\plmac@newcommand@i| % with a first argument of ``|*|'' for |\perl|[|re|]|newcommand*| or % ``|!|'' for ordinary |\perl|[|re|]|newcommand|. % % \item |\plmac@newcommand@i| defines |\plmac@starchar| as ``|*|'' if % it was passed a ``|*|'' or \meta{empty} if it was passed a ``|!|''. % It then stores the name of the user's macro in |\plmac@macname|, a % |\write|able version of the name in |\plmac@cleaned@macname|, and % the macro's previous definition (needed by |\perlrenewcommand|) in % |\plmac@oldbody|. Finally, |\plmac@newcommand@i| invokes % |\plmac@newcommand@ii|. % % \item |\plmac@newcommand@ii| stores the number of arguments to the % user's macro (which may be zero) in |\plmac@numargs|. It then % invokes |\plmac@newcommand@iii@opt| if the first argument is % supposed to be optional or |\plmac@newcommand@iii@no@opt| if all % arguments are supposed to be required. % % \item |\plmac@newcommand@iii@opt| defines |\plmac@defarg| as the % default value of the optional argument. % |\plmac@newcommand@iii@no@opt| defines it as \meta{empty}. Both % functions then call |\plmac@haveargs|. % % \item |\plmac@haveargs| stores the user's macro body (written in % Perl) verbatim in |\plmac@perlcode|. |\plmac@haveargs| then invokes % |\plmac@havecode|. % % \item By the time |\plmac@havecode| is invoked all of the % information needed to define the user's macro is available. Before % defining a \LaTeX{} macro, however, |\plmac@havecode| invokes % |\plmac@write@perl| to tell \perltex{} to define a Perl subroutine % with a name based on |\plmac@cleaned@macname| and the code contained % in |\plmac@perlcode|. Figure~\ref{fig:tofile-define} illustrates % the data that |\plmac@write@perl| passes to \perltex. % % \begin{figure}[htbp] % \centering % \DeleteShortVerb\| % \begin{tabular}{|l|} % \hline % \verb|DEF| \\ \hline % \verb|\plmac@tag| \\ \hline % \verb|\plmac@cleaned@macname| \\ \hline % \verb|\plmac@tag| \\ \hline % \verb|\plmac@perlcode| \\ \hline % \end{tabular} % \MakeShortVerb\| % \caption{Data written to \texttt{\string\plmac@tofile} to define a % Perl subroutine} % \label{fig:tofile-define} % \end{figure} % % \item |\plmac@havecode| invokes |\newcommand| or |\renewcommand|, as % appropriate, defining the user's macro as a call to % |\plmac@write@perl|. An invocation of the user's \LaTeX{} macro % causes |\plmac@write@perl| to pass the information shown in % Figure~\ref{fig:tofile-use} to \perltex. % % \begin{figure}[htbp] % \centering % \DeleteShortVerb\| % \begin{tabular}{|l|} % \hline % \verb|USE| \\ \hline % \verb|\plmac@tag| \\ \hline % \verb|\plmac@cleaned@macname| \\ \hline % \verb|\plmac@tag| \\ \hline % \verb|#1| \\ \hline % \verb|\plmac@tag| \\ \hline % \verb|#2| \\ \hline % \verb|\plmac@tag| \\ \hline % \verb|#3| \\ \hline % \multicolumn{1}{c}{$\vdots$} \\ \hline % \verb|#|\meta{last} \\ \hline % \end{tabular} % \MakeShortVerb\| % \caption{Data written to \texttt{\string\plmac@tofile} to invoke a % Perl subroutine} % \label{fig:tofile-use} % \end{figure} % % \item Whenever |\plmac@write@perl| is invoked it writes its argument % verbatim to |\plmac@tofile|; \perltex{} evaluates the code and % writes |\plmac@fromfile|; finally, |\plmac@write@perl| |\input|s % |\plmac@fromfile|. % \end{enumerate} % % An example might help distinguish the myriad macros used internally by % \perlmac. Consider the following call made by the user's % document:\label{text:example-cmd} % % \begin{center} % |\perlnewcommand*{\example}[3][frobozz]{join("---", @_)}| % \end{center} % % \noindent % Table~\ref{tbl:example-cmd} shows how \perlmac{} parses that command % into its constituent components and which components are bound to % which \perlmac{} macros. % % \begin{table}[htbp] % \centering % \caption{Macro assignments corresponding to an sample % \texttt{\string\perlnewcommand*}} % \label{tbl:example-cmd} % \begin{tabular}{@{}lll@{}} % \hline % Macro & \multicolumn{2}{l@{}}{Sample definition} \\ % \hline % |\plmac@command| & |\newcommand| \\ % |\plmac@starchar| & |*| \\ % |\plmac@macname| & |\example| \\ % |\plmac@cleaned@macname| & |\example| & (catcode~11) \\ % |\plmac@oldbody| & |\relax| & (presumably) \\ % |\plmac@numargs| & |3| \\ % |\plmac@defarg| & |frobozz| \\ % |\plmac@perlcode| & |join("---", @_)| & (catcode~11) \\ % \hline % \end{tabular} % \end{table} % % \bigskip % % \begin{macro}{\perlnewcommand} % \begin{macro}{\perlrenewcommand} % \begin{macro}{\plmac@command} % \begin{macro}{\plmac@next} % |\perlnewcommand| and |\perlrenewcommand| are the first two commands % exported to the user by \perlmac. |\perlnewcommand| is analogous to % |\newcommand| except that the macro body consists of Perl code instead % of \LaTeX{} code. Likewise, |\perlrenewcommand| is analogous to % |\renewcommand| except that the macro body consists of Perl code % instead of \LaTeX{} code. |\perlnewcommand| and |\perlrenewcommand| % merely define |\plmac@command| and |\plmac@next| and invoke % |\plmac@newcommand@i|. % \begin{macrocode} \def\perlnewcommand{% \let\plmac@command=\newcommand \let\plmac@next=\relax \@ifnextchar*{\plmac@newcommand@i}{\plmac@newcommand@i!}% } % \end{macrocode} % \begin{macrocode} \def\perlrenewcommand{% \let\plmac@next=\relax \let\plmac@command=\renewcommand \@ifnextchar*{\plmac@newcommand@i}{\plmac@newcommand@i!}% } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\plmac@newcommand@i} % \begin{macro}{\plmac@starchar} % \begin{macro}{\plmac@macname} % \begin{macro}{\plmac@oldbody} % \begin{macro}{\plmac@cleaned@macname} % If\label{page:newcommand-i} the user invoked % |\perl|[|re|]|newcommand*| then |\plmac@newcommand@i| is passed a % ``|*|'' and, in turn, defines |\plmac@starchar| as ``|*|''. If the % user invoked |\perl|[|re|]|newcommand| (no ``|*|'') then % |\plmac@newcommand@i| is passed a ``|!|'' and, in turn, defines % |\plmac@starchar| as \meta{empty}. In either case, % |\plmac@newcommand@i| defines |\plmac@macname| as the name of the % user's macro, |\plmac@cleaned@macname| as a |\write|able % (i.e.,~category code~11) version of |\plmac@macname|, and % |\plmac@oldbody| and the previous definition of the user's macro. % (|\plmac@oldbody| is needed by |\perlrenewcommand|.) It then invokes % |\plmac@newcommand@ii|. % \begin{macrocode} \def\plmac@newcommand@i#1#2{% \ifx#1*% \def\plmac@starchar{*}% \else \def\plmac@starchar{}% \fi \def\plmac@macname{#2}% \let\plmac@oldbody=#2\relax \expandafter\def\expandafter\plmac@cleaned@macname\expandafter{% \expandafter\string\plmac@macname}% \@ifnextchar[{\plmac@newcommand@ii}{\plmac@newcommand@ii[0]}%] } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\plmac@newcommand@ii} % \begin{macro}{\plmac@numargs} % |\plmac@newcommand@i| invokes |\plmac@newcommand@ii| with the number % of arguments to the user's macro in brackets. |\plmac@newcommand@ii| % stores that number in |\plmac@numargs| and invokes % |\plmac@newcommand@iii@opt| if the first argument is to be optional or % |\plmac@newcommand@iii@no@opt| if all arguments are to be mandatory. % \begin{macrocode} \def\plmac@newcommand@ii[#1]{% \def\plmac@numargs{#1}% \@ifnextchar[{\plmac@newcommand@iii@opt} {\plmac@newcommand@iii@no@opt}%] } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\plmac@newcommand@iii@opt} % \begin{macro}{\plmac@newcommand@iii@no@opt} % \begin{macro}{\plmac@defarg} % Only one of these two macros is executed per invocation of % |\perl|[|re|]|newcommand|, depending on whether or not the first % argument of the user's macro is an optional argument. % |\plmac@newcommand@iii@opt| is invoked if the argument is optional. % It defines |\plmac@defarg| to the default value of the optional % argument. |\plmac@newcommand@iii@no@opt| is invoked if all arguments % are mandatory. It defines |\plmac@defarg| as |\relax|. Both % |\plmac@newcommand@iii@opt| and |\plmac@newcommand@iii@no@opt| then % invoke |\plmac@haveargs|. % \begin{macrocode} \def\plmac@newcommand@iii@opt[#1]{% \def\plmac@defarg{#1}% \plmac@haveargs } % \end{macrocode} % \begin{macrocode} \def\plmac@newcommand@iii@no@opt{% \let\plmac@defarg=\relax \plmac@haveargs } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\plmac@perlcode} % \begin{macro}{\plmac@haveargs} % Now things start to get tricky. We have all of the arguments we need % to define the user's command so all that's left is to grab the macro % body. But there's a catch: Valid Perl code is unlikely to be valid % \LaTeX{} code. We therefore have to read the macro body in a % |\verb|-like mode. Furthermore, we actually need to \emph{store} the % macro body in a variable, as we don't need it right away. % % The approach we take in |\plmac@haveargs| is as follows. First, we % give all ``special'' characters category code~12 (``other''). We then % indicate that the carriage return character (control-M) marks the end % of a line and that curly braces retain their normal meaning. With the % aforementioned category-code definitions, we now have to store the % next curly-brace-delimited fragment of text, end the current group to % reset all category codes to their previous value, and continue % processing the user's macro definition. How do we do that? The % answer is to assign the upcoming text fragment to a token register % (|\plmac@perlcode|) while an |\afterassignment| is in effect. The % |\afterassignment| causes control to transfer to |\plmac@havecode| % right after |\plmac@perlcode| receives the macro body with all of the % ``special'' characters made impotent. % \begin{macrocode} \newtoks\plmac@perlcode % \end{macrocode} % \begin{macrocode} \def\plmac@haveargs{% \begingroup \let\do\@makeother\dospecials \catcode`\^^M=\active \newlinechar`\^^M \endlinechar=`\^^M \catcode`\{=1 \catcode`\}=2 \afterassignment\plmac@havecode \global\plmac@perlcode } % \end{macrocode} % \end{macro} % \end{macro} % % Control is transfered to |\plmac@havecode| from |\plmac@haveargs| % right after the user's macro body is assigned to |\plmac@perlcode|. % We now have everything we need to define the user's macro. The goal % is to define it as ``|\plmac@write@perl{|\meta{contents of % Figure~\ref{fig:tofile-use}}|}|''. This is easier said than done % because the number of arguments in the user's macro is not known % statically, yet we need to iterate over however many arguments there % are. Because of this complexity, we will explain |\plmac@perlcode| % piece-by-piece. % % \begin{macro}{\plmac@sep} % Define a character to separate each of the items presented in % Figures~\ref{fig:tofile-define} and~\ref{fig:tofile-use}. Perl will % need to strip this off each argument. For convenience in porting to % languages with less powerful string manipulation than Perl's, we % define |\plmac@sep| as a carriage-return character of category code~11 % (``letter''). % \begin{macrocode} {\catcode`\^^M=11\gdef\plmac@sep{^^M}} % \end{macrocode} % \end{macro} % % \begin{macro}{\plmac@argnum} % Define a loop variable that will iterate from |1| to the number of % arguments in the user's function, i.e.,~|\plmac@numargs|. % \begin{macrocode} \newcount\plmac@argnum % \end{macrocode} % \end{macro} % % \begin{macro}{\plmac@havecode} % Now comes the final piece of what started as a call to % |\perl|[|re|]|newcommand|. First, to reset all category codes back to % normal, |\plmac@havecode| ends the group that was begun in % |\plmac@haveargs|. % \changes{v1.1}{2004/02/24}{Added a \texttt{\string\string\string\plmac@next} % hook to support \PerlTeX's new environment-defining macros} % \begin{macrocode} \def\plmac@havecode{% \endgroup % \end{macrocode} % % \begin{macro}{\plmac@define@sub} % We invoke |\plmac@write@perl| to define a Perl subroutine named after % |\plmac@cleaned@macname|. |\plmac@define@sub| sends Perl the % information shown in Figure~\vref{fig:tofile-define}. % \begin{macrocode} \edef\plmac@define@sub{% \noexpand\plmac@write@perl{DEF\plmac@sep \plmac@tag\plmac@sep \plmac@cleaned@macname\plmac@sep \plmac@tag\plmac@sep \the\plmac@perlcode }% }% \plmac@define@sub % \end{macrocode} % \end{macro} % % \begin{macro}{\plmac@body} % The rest of |\plmac@havecode| is preparation for defining the user's % macro. (\LaTeXe's |\newcommand| or |\renewcommand| will do the actual % work, though.) |\plmac@body| will eventually contain the complete % (\LaTeX) body of the user's macro. Here, we initialize it to the % first three items listed in Figure~\vref{fig:tofile-use} (with % intervening |\plmac@sep|s). % \begin{macrocode} \edef\plmac@body{% USE\plmac@sep \plmac@tag\plmac@sep \plmac@cleaned@macname }% % \end{macrocode} % % \begin{macro}{\plmac@hash} % Now, for each argument |#1|, |#2|,~\dots, |#\plmac@numargs| we append % a |\plmac@tag| plus the argument to |\plmac@body| (as always, with a % |\plmac@sep| after each item). This requires more trickery, as \TeX{} % requires a macro-parameter character (``|#|'') to be followed by a % literal number, not a variable. The approach we take, which I first % discovered in the Texinfo source code (although it's used by \LaTeX{} % and probably other \TeX-based systems as well), is to |\let|-bind % |\plmac@hash| to |\relax|. This makes |\plmac@hash| unexpandable, and % because it's not a ``|#|'', \TeX{} doesn't complain. After % |\plmac@body| has been extended to include |\plmac@hash1|, % |\plmac@hash2|,~\dots, |\plmac@hash\plmac@numargs|, we then % |\let|-bind |\plmac@hash| to |##|, which \TeX{} lets us do because % we're within a macro definition (|\plmac@havecode|). |\plmac@body| % will then contain |#1|, |#2|,~\dots, |#\plmac@numargs|, as desired. % \begin{macrocode} \let\plmac@hash=\relax \plmac@argnum=\@ne \loop \ifnum\plmac@numargs<\plmac@argnum \else \edef\plmac@body{% \plmac@body\plmac@sep\plmac@tag\plmac@sep \plmac@hash\plmac@hash\number\plmac@argnum}% \advance\plmac@argnum by \@ne \repeat \let\plmac@hash=##% % \end{macrocode} % % \begin{macro}{\plmac@define@command} % We're ready to execute a |\|[|re|]|newcommand|. Because we need to % expand many of our variables, we |\edef| |\plmac@define@command| to % the appropriate |\|[|re|]|newcommand| call, which we will soon % execute. The user's macro must first be |\let|-bound to |\relax| to % prevent it from expanding. Then, we handle two cases: either all % arguments are mandatory (and |\plmac@defarg| is |\relax|) or the % user's macro has an optional argument (with default value % |\plmac@defarg|). % \begin{macrocode} \expandafter\let\plmac@macname=\relax \ifx\plmac@defarg\relax \edef\plmac@define@command{% \noexpand\plmac@command\plmac@starchar{\plmac@macname}% [\plmac@numargs]{% \noexpand\plmac@write@perl{\plmac@body}% }% }% \else \edef\plmac@define@command{% \noexpand\plmac@command\plmac@starchar{\plmac@macname}% [\plmac@numargs][\plmac@defarg]{% \noexpand\plmac@write@perl{\plmac@body}% }% }% \fi % \end{macrocode} % % The final steps are to restore the previous definition of the user's % macro---we had set it to |\relax| above to make the name % unexpandable---then redefine it by invoking |\plmac@define@command|. % Why do we need to restore the previous definition if we're just going % to redefine it? Because |\newcommand| needs to produce an error if % the macro was previously defined and |\renewcommand| needs to produce % an error if the macro was \emph{not} previously defined. % % |\plmac@havecode| concludes by invoking |\plmac@next|, which is a % no-op for |\perlnewcommand| and |\perlrenewcommand| but processes the % end-environment code for |\perlnewenvironment| and % |\perlrenewenvironment|. % \begin{macrocode} \expandafter\let\plmac@macname=\plmac@oldbody \plmac@define@command \plmac@next } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % \subsubsection{Defining Perl environments} % \label{sec:perl-env} % % \changes{v1.1}{2004/02/24}{Added new % \texttt{\string\string\string\perlnewenvironment} and % \texttt{\string\string\string\perlrenewenvironment} macros} % % Section~\ref{sec:perl-mac} detailed the implementation of % |\perlnewcommand| and |\perlrenewcommand|. Section~\ref{sec:perl-env} % does likewise for |\perlnewenvironment| and |\perlrenewenvironment|, % which are the Perl-bodied analogues of |\newenvironment| and % |\renewenvironment|. This section is significantly shorter than the % previous because |\perlnewenvironment| and |\perlrenewenvironment| are % largely built atop the macros already defined in % Section~\ref{sec:perl-mac}. % % \begin{macro}{\perlnewenvironment} % \begin{macro}{\perlrenewenvironment} % \begin{macro}{\plmac@command} % \begin{macro}{\plmac@next} % |\perlnewenvironment| and |\perlrenewenvironment| are the remaining % two commands exported to the user by \perlmac. |\perlnewenvironment| % is analogous to |\newenvironment| except that the macro body consists % of Perl code instead of \LaTeX{} code. Likewise, % |\perlrenewenvironment| is analogous to |\renewenvironment| except % that the macro body consists of Perl code instead of \LaTeX{} code. % |\perlnewenvironment| and |\perlrenewenvironment| merely define % |\plmac@command| and |\plmac@next| and invoke % |\plmac@newenvironment@i|. % % The significance of |\plmac@next| (which was let-bound to |\relax| for % |\perl|[|re|]|newcommand| but is let-bound to |\plmac@end@environment| % here) is that a \LaTeX{} environment definition is really two macro % definitions: |\|\meta{name} and |\end|\meta{name}. Because we want to % reuse as much code as possible the idea is to define the ``begin'' % code as one macro, then inject---by way of |plmac@next|---a call to % |\plmac@end@environment|, which defines the ``end'' code as a second % macro. % \begin{macrocode} \def\perlnewenvironment{% \let\plmac@command=\newcommand \let\plmac@next=\plmac@end@environment \@ifnextchar*{\plmac@newenvironment@i}{\plmac@newenvironment@i!}% } % \end{macrocode} % \begin{macrocode} \def\perlrenewenvironment{% \let\plmac@command=\renewcommand \let\plmac@next=\plmac@end@environment \@ifnextchar*{\plmac@newenvironment@i}{\plmac@newenvironment@i!}% } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\plmac@newenvironment@i} % \begin{macro}{\plmac@starchar} % \begin{macro}{\plmac@envname} % \begin{macro}{\plmac@macname} % \begin{macro}{\plmac@oldbody} % \begin{macro}{\plmac@cleaned@macname} % The |\plmac@newenvironment@i| macro is analogous to % |\plmac@newcommand@i|; see the description of % |\plmac@newcommand@i|~\vpageref{page:newcommand-i} to understand the % basic structure. The primary difference is that the environment name % (|#2|) is just text, not a control sequence. We store this text in % |\plmac@envname| to facilitate generating the names of the two macros % that constitute an environment definition. Note that there is no % |\plmac@newenvironment@ii|; control passes instead to % |\plmac@newcommand@ii|. % \begin{macrocode} \def\plmac@newenvironment@i#1#2{% \ifx#1*% \def\plmac@starchar{*}% \else \def\plmac@starchar{}% \fi \def\plmac@envname{#2}% \expandafter\def\expandafter\plmac@macname\expandafter{\csname#2\endcsname}% \expandafter\let\expandafter\plmac@oldbody\plmac@macname\relax \expandafter\def\expandafter\plmac@cleaned@macname\expandafter{% \expandafter\string\plmac@macname}% \@ifnextchar[{\plmac@newcommand@ii}{\plmac@newcommand@ii[0]}%] } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\plmac@end@environment} % \begin{macro}{\plmac@next} % \begin{macro}{\plmac@macname} % \begin{macro}{\plmac@oldbody} % \begin{macro}{\plmac@cleaned@macname} % Recall that an environment definition is a shortcut for two macro % definitions: |\|\meta{name} and |\end|\meta{name} (where \meta{name} % was stored in |\plmac@envname| by |\plmac@newenvironment@i|). After % defining |\|\meta{name}, |\plmac@havecode| transfers control to % |\plmac@end@environment| because |\plmac@next| was let-bound to % |\plmac@end@environment| in |\perl|[|re|]|newenvironment|. % % |\plmac@end@environment|'s purpose is to define |\end|\meta{name}. % This is a little tricky, however, because \LaTeX's % |\|[|re|]|newcommand| refuses to (re)define a macro whose name begins % with ``|end|''. The solution that |\plmac@end@environment| takes is % first to define a |\plmac@end@macro| macro then (in |plmac@next|) % let-bind |\end|\meta{name} to it. Other than that, % |\plmac@end@environment| is a combined and simplified version of % |\perlnewenvironment|, |\perlrenewenvironment|, and % |\plmac@newenvironment@i|. % \begin{macrocode} \def\plmac@end@environment{% \expandafter\def\expandafter\plmac@next\expandafter{\expandafter \let\csname end\plmac@envname\endcsname=\plmac@end@macro \let\plmac@next=\relax }% \def\plmac@macname{\plmac@end@macro}% \expandafter\let\expandafter\plmac@oldbody\csname end\plmac@envname\endcsname \expandafter\def\expandafter\plmac@cleaned@macname\expandafter{% \expandafter\string\plmac@macname}% \@ifnextchar[{\plmac@newcommand@ii}{\plmac@newcommand@ii[0]}%] } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % \subsubsection{Executing top-level Perl code} % \label{sec:perl-run} % % The macros defined in Sections~\ref{sec:perl-mac} % and~\ref{sec:perl-env} enable an author to inject subroutines into the % Perl sandbox. The final \PerlTeX\ macro, |\perldo|, instructs the % Perl sandbox to execute a block of code outside of all subroutines. % |\perldo|'s implementation is much simpler than that of the other % author macros because |\perldo| does not have to process subroutine % arguments. Figure~\ref{fig:tofile-run} illustrates the data that gets % written to |plmac@tofile| (indirectly) by |\perldo|. % % \begin{figure}[htbp] % \centering % \DeleteShortVerb\| % \begin{tabular}{|l|} % \hline % \verb|RUN| \\ \hline % \verb|\plmac@tag| \\ \hline % \textit{Ignored} \\ \hline % \verb|\plmac@tag| \\ \hline % \verb|\plmac@perlcode| \\ \hline % \end{tabular} % \MakeShortVerb\| % \caption{Data written to \texttt{\string\plmac@tofile} to execute % Perl code} % \label{fig:tofile-run} % \end{figure} % % % \begin{macro}{\perldo} % Execute a block of Perl code and pass the result to \LaTeX\ for % further processing. This code is nearly identical to that of % Section~\ref{sec:perl-mac}'s |\plmac@haveargs| but ends by invoking % |\plmac@have@run@code| instead of |\plmac@havecode|. % \changes{v1.3}{2006/06/23}{Introduced \texttt{\string\string\string\perldo} % to support code execution outside of all subroutines.} % \begin{macrocode} \def\perldo{% \begingroup \let\do\@makeother\dospecials \catcode`\^^M=\active \newlinechar`\^^M \endlinechar=`\^^M \catcode`\{=1 \catcode`\}=2 \afterassignment\plmac@have@run@code \global\plmac@perlcode } % \end{macrocode} % \end{macro} % % \begin{macro}{\plmac@have@run@code} % \begin{macro}{\plmac@run@code} % Pass a block of code to Perl to execute. |\plmac@have@run@code| is % identical to |\plmac@havecode| but specifies the |RUN| tag instead of % the |DEF| tag. % \changes{v1.3}{2006/06/23}{Added to assist % \texttt{\string\string\string\perldo}} % \begin{macrocode} \def\plmac@have@run@code{% \endgroup \edef\plmac@run@code{% \noexpand\plmac@write@perl{RUN\plmac@sep \plmac@tag\plmac@sep N/A\plmac@sep \plmac@tag\plmac@sep \the\plmac@perlcode }% }% \plmac@run@code } % \end{macrocode} % \end{macro} % \end{macro} % % % \subsubsection{Communication between \LaTeX{} and Perl} % % As shown in the previous section, when a document invokes % |\perl|[|re|]|newcommand| to define a macro, \perlmac{} defines the % macro in terms of a call to |\plmac@write@perl|. In this section, we % learn how |\plmac@write@perl| operates. % % At the highest level, \LaTeX-to-Perl communication is performed via % the filesystem. In essence, \LaTeX{} writes a file (|\plmac@tofile|) % corresponding to the information in either % Figure~\ref{fig:tofile-define} or Figure~\ref{fig:tofile-use}; Perl % reads the file, executes the code within it, and writes a |.tex| file % (|\plmac@fromfile|); and, finally, \LaTeX{} reads and executes the new % |.tex| file. However, the actual communication protocol is a bit more % involved than that. The problem is that Perl needs to know when % \LaTeX{} has finished writing Perl code and \LaTeX{} needs to know % when Perl has finished writing \LaTeX{} code. The solution involves % introducing three extra files---|\plmac@toflag|, |\plmac@fromflag|, % and |\plmac@doneflag|---which are used exclusively for \LaTeX-to-Perl % synchronization. % % There's a catch: Although Perl can create and delete files, \LaTeX{} % can only create them. Even worse, \LaTeX{} (more specifically, % te\TeX, which is the \TeX{} distribution under which I developed % \PerlTeX) cannot reliably poll for a file's \emph{non}existence; if a % file is deleted in the middle of an |\immediate\openin|, |latex| % aborts with an error message. These restrictions led to the % regrettably convoluted protocol illustrated in % Figure~\ref{fig:comm-protocol}. In the figure, ``Touch'' means % ``create a zero-length file''; ``Await'' means ``wait until the file % exists''; and, ``Read'', ``Write'', and ``Delete'' are defined as % expected. Assuming the filesystem performs these operations in a % sequentially consistent order (not necessarily guaranteed on all % filesystems, unfortunately), \PerlTeX{} should behave as expected. % % \begin{figure}[htbp] % \centering % \makeatletter\setlength{\unitlength}{\baselineskip}\makeatother % \DeleteShortVerb\| % \def\topl{\texttt{\string\plmac@tofile}} % \def\frpl{\texttt{\string\plmac@fromfile}} % \def\tfpl{\texttt{\string\plmac@toflag}} % \def\ffpl{\texttt{\string\plmac@fromflag}} % \def\dfpl{\texttt{\string\plmac@doneflag}} % \begin{tabular}{@{}c@{\hspace*{1cm}}|l@{~}l|c|l@{~}l|@{}} % \multicolumn{1}{@{}c@{\hspace*{1cm}}}{Time} & % \multicolumn{2}{c}{\LaTeX} & % \multicolumn{1}{c}{} & % \multicolumn{2}{c@{}}{Perl} \\ % % \cline{2-3}\cline{5-6} % \smash{\vector(0,-1){11}} % & Write & \topl & & & \\ % & Touch & \tfpl & $\rightarrow$ & Await & \tfpl \\ % & & & & Read & \topl \\ % & & & & Write & \frpl \\ % & & & & Delete & \tfpl \\ % & & & & Delete & \topl \\ % & & & & Delete & \dfpl \\ % & Await & \ffpl & $\leftarrow$ & Touch & \ffpl \\ % & Touch & \topl & $\rightarrow$ & Await & \topl \\ % & & & & Delete & \ffpl \\ % & Await & \dfpl & $\leftarrow$ & Touch & \dfpl \\ % & Read & \frpl & & & \\ % \cline{2-3}\cline{5-6} % \end{tabular} % \MakeShortVerb\| % \caption{\LaTeX-to-Perl communication protocol} % \label{fig:comm-protocol} % \end{figure} % % Although Figure~\ref{fig:comm-protocol} shows the read of % |\plmac@fromfile| as the final step of the protocol, the file's % contents are in fact valid as soon as \LaTeX{} detects that % |\plmac@fromflag| exists. Deferring the read to the end, however, % enables \PerlTeX{} to support recursive macro invocations. % % \begin{macro}{\plmac@infile} % \begin{macro}{\plmac@IfFileExists} % \changes{v2.3}{2024/12/03}{Introduce this macro, which implements a % non-caching version of \string\cs{IfFileExists}} % The Await operations in Figure~\ref{fig:comm-protocol} require testing % if a file exists. On the \LaTeX\ side, this normally would be % achieved using \LaTeX's \cs{IfFileExists} macro, and this is indeed % what \PerlTeX\ did until version~2.2. However, the \mbox{1-Jun-2023} % release of \LaTeX3 introduced a performance optimization that lets % \cs{IfFileExists} cache prior results. (See % \url{https://www.latex-project.org/news/latex2e-news/ltnews37.pdf}.) % In other words, once \cs{IfFileExists} determines that a file exists, % it will follow the \textsc{true} branch on all subsequent calls % without ever re-checking if the file still exists. This semantics % breaks the protocol described in Figure~\ref{fig:comm-protocol} by % enabling Await to return before the file being waited for actually % exists. % % To work around \LaTeX3's new behavior, we define our own version of % \cs{IfFileExists} called \cs{plmac@IfFileExists}, which is derived % from \cs{IfFileExists}'s simpler, more straightforward % \LaTeXe\ implementation. In particular, file existence is checked % explicitly on each invocation. % \begin{macrocode} \newread\plmac@infile % \end{macrocode} % \begin{macrocode} \newcommand{\plmac@IfFileExists}[3]{% \openin\plmac@infile=#1 % \ifeof\plmac@infile \def\plmac@next{#3}% \else \closein\plmac@infile \def\plmac@next{#2}% \fi \plmac@next } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\plmac@await@existence} % \changes{v1.9}{2009/09/13}{Put the % \texttt{\string\string\string\input\string\string\string\plmac@pipe} % within an \texttt{lrbox} environment to prevent a partial read from % introducing spurious text into the document} % \begin{macro}{\ifplmac@file@exists} % \begin{macro}{\plmac@file@existstrue} % \begin{macro}{\plmac@file@existsfalse} % The purpose of the \cs{plmac@await@existence} macro is repeatedly to % check the existence of a given file until the file actually exists. % We use \cs{plmac@IfFileExists} (defined above) to check if the file % exists and accordingly either continue or exit the loop. % % As a performance optimization we |\input| a named pipe. This causes % the |latex| process to relinquish the CPU until the |perltex| process % writes data (always just a comment plus ``|\endinput|'') into the % named pipe. On systems that don't support persistent named pipes % (e.g.,~Microsoft Windows), |\plmac@pipe| is an ordinary file % containing only a comment plus ``|\endinput|''. While reading that % file is not guaranteed to relinquish the CPU, it should not hurt the % performance or correctness of the communication protocol between % \LaTeX\ and Perl. % \changes{v1.5}{2007/10/13}{Modified to read from a named pipe before % checking file existence} % \begin{macrocode} \newif\ifplmac@file@exists % \end{macrocode} % \begin{macrocode} \newcommand{\plmac@await@existence}[1]{% \begin{lrbox}{\@tempboxa}% \input\plmac@pipe \end{lrbox}% \loop \plmac@IfFileExists{#1}% {\plmac@file@existstrue}% {\plmac@file@existsfalse}% \ifplmac@file@exists \else \repeat } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\plmac@outfile} % We define a file handle for |\plmac@write@perl@i| to use to create and % write |\plmac@tofile| and |\plmac@toflag|. % \begin{macrocode} \newwrite\plmac@outfile % \end{macrocode} % \end{macro} % % \begin{macro}{\plmac@write@perl} % |\plmac@write@perl| begins the \LaTeX-to-Perl data exchange, following % the protocol illustrated in Figure~\ref{fig:comm-protocol}. % |\plmac@write@perl| prepares for the next piece of text in the input % stream to be read with ``special'' characters marked as category % code~12 (``other''). This prevents \LaTeX{} from complaining if the % Perl code contains invalid \LaTeX{} (which it usually will). % |\plmac@write@perl| ends by passing control to |\plmac@write@perl@i|, % which performs the bulk of the work. % \begin{macrocode} \newcommand{\plmac@write@perl}{% \begingroup \let\do\@makeother\dospecials \catcode`\^^M=\active \newlinechar`\^^M \endlinechar=`\^^M \catcode`\{=1 \catcode`\}=2 \plmac@write@perl@i } % \end{macrocode} % \end{macro} % % \begin{macro}{\plmac@write@perl@i} % When |\plmac@write@perl@i| begins executing, the category codes are % set up so that the macro's argument will be evaluated ``verbatim'' % except for the part consisting of the \LaTeX\ code passed in by the % author, which is partially expanded. Thus, everything is in place for % |\plmac@write@perl@i| to send its argument to Perl and read back the % (\LaTeX) result. % % Because\label{page:define-dummies} all of \perlmac's protocol % processing is encapsulated within |\plmac@write@perl@i|, this is the % only macro that strictly requires \perltex. Consequently, we wrap the % entire macro definition within a check for \perltex. % \changes{v1.8}{2009/03/25}{Renamed % \texttt{\string\string\string\ifplmac@have@perltex} to % \texttt{\char`\\ ifperl} to help authors write % mixed \string\LaTeX\string\slash\space \string\PerlTeX\ documents} % \begin{macrocode} \ifperl \newcommand{\plmac@write@perl@i}[1]{% % \end{macrocode} % The first step is to write argument |#1| to |\plmac@tofile|: % \changes{v1.1}{2004/02/19}{Made argument-handling more rational by % making \texttt{\string\string\string\protect}, % \texttt{\string\string\string\begin}, and % \texttt{\string\string\string\end} non-expandable} % \begin{macrocode} \immediate\openout\plmac@outfile=\plmac@tofile\relax \let\protect=\noexpand \def\begin{\noexpand\begin}% \def\end{\noexpand\end}% \immediate\write\plmac@outfile{#1}% \immediate\closeout\plmac@outfile % \end{macrocode} % \noindent % (In the future, it might be worth redefining |\def|, |\edef|, |\gdef|, % |\xdef|, |\let|, and maybe some other control sequences as % ``|\noexpand|\meta{control sequence}|\noexpand|'' so that |\write| % doesn't try to expand an undefined control sequence.) % % We're now finished using |#1| so we can end the group begun by % |\plmac@write@perl|, thereby resetting each character's category code % back to its previous value. % \begin{macrocode} \endgroup % \end{macrocode} % % Continuing the protocol illustrated in Figure~\ref{fig:comm-protocol}, % we create a zero-byte |\plmac@toflag| in order to notify \perltex{} % that it's now safe to read |\plmac@tofile|. % \begin{macrocode} \immediate\openout\plmac@outfile=\plmac@toflag\relax \immediate\closeout\plmac@outfile % \end{macrocode} % % To avoid reading |\plmac@fromfile| before \perltex{} has finished % writing it we must wait until \perltex{} creates |\plmac@fromflag|, % which it does only after it has written |\plmac@fromfile|. % \begin{macrocode} \plmac@await@existence\plmac@fromflag % \end{macrocode} % % At this point, |\plmac@fromfile| should contain valid \LaTeX{} code. % However, we defer inputting it until we the very end. Doing so % enables recursive and mutually recursive invocations of \PerlTeX{} % macros. % \changes{v1.2}{2004/10/07}{Moved the % \texttt{\string\string\string\input} of the generated Perl code to % the end of the routine in order to support recursive \PerlTeX{} % macro invocations.} % % Because \TeX{} can't delete files we require an additional % \LaTeX-to-Perl synchronization step. For convenience, we recycle % |\plmac@tofile| as a synchronization file rather than introduce yet % another flag file to complement |\plmac@toflag|, |\plmac@fromflag|, % and |\plmac@doneflag|. % \begin{macrocode} \immediate\openout\plmac@outfile=\plmac@tofile\relax \immediate\closeout\plmac@outfile \plmac@await@existence\plmac@doneflag % \end{macrocode} % % The only thing left to do is to |\input| and evaluate % |\plmac@fromfile|, which contains the \LaTeX{} output from the Perl % subroutine. % \begin{macrocode} \input\plmac@fromfile\relax } % \end{macrocode} % \end{macro} % % \begin{macro}{\plmac@write@perl@i} % \changes{v1.1}{2004/02/24}{Added a dummy version of the macro to use % if \texttt{latex} was launched directly, without \perltex} % The foregoing code represents the ``real'' definition of % |\plmac@write@perl@i|. For the user's convenience, we define a dummy % version of |\plmac@write@perl@i| so that a document which utilizes % \perlmac{} can still compile even if not built using \perltex. All % calls to macros defined with |\perl|[|re|]|newcommand| and all % invocations of environments defined with |\perl|[|re|]|newenvironment| % are replaced with ``\fbox{Perl\TeX}''. A minor complication is that % text can't be inserted before the |\begin{document}|. Hence, we % initially define |\plmac@write@perl@i| as a do-nothing macro and % redefine it as ``|\fbox{Perl\TeX}|'' at the |\begin{document}|. % \begin{macrocode} \else \newcommand{\plmac@write@perl@i}[1]{\endgroup} % \end{macrocode} % \begin{macro}{\plmac@show@placeholder} % There's really no point in outputting a framed ``Perl\TeX'' when a % macro is defined \emph{and} when it's used. |\plmac@show@placeholder| % checks the first character of the protocol header. If it's % ``D''~(|DEF|), nothing is output. Otherwise, it'll be ``U''~(|USE|) % and ``Perl\TeX'' will be output. % \begin{macrocode} \gdef\plmac@show@placeholder#1#2\@empty{% \ifx#1D\relax \endgroup \else \endgroup \fbox{Perl\TeX}% \fi }% % \end{macrocode} % \end{macro} % \begin{macrocode} \AtBeginDocument{% \renewcommand{\plmac@write@perl@i}[1]{% \plmac@show@placeholder#1\@empty }% } \fi % \end{macrocode} % \end{macro} % % \iffalse % % \fi % % ^^A Keep track of the number of lines of code in perltex. % \makeatletter % \immediate\write\@auxout{^^A % \noexpand\gdef\noexpand\perlmaclines{\the\c@CodelineNo}} % \makeatother % % % \subsection{\perltex} % \label{sec:perltex} % % \perltex{} is a wrapper script for |latex| (or any other \LaTeX{} % compiler). It sets up client-server communication between \LaTeX{} % and Perl, with \LaTeX{} as the client and Perl as the server. When a % \LaTeX{} document sends a piece of Perl code to \perltex{} (with the % help of \perlmac, as detailed in Section~\ref{sec:perlmacros}), % \perltex{} executes it within a secure sandbox and transmits the % resulting \LaTeX{} code back to the document. % \changes{v1.0a}{2003/08/21}{Made all \texttt{unlink} calls wait for the % file to actually disappear} % % \iffalse %<*perltex> % \fi % % \subsubsection{Header comments} % % Because \perltex{} is generated without a \textsf{DocStrip} % preamble or postamble we have to manually include the desired % text as Perl comments. % % \begin{macrocode} #! /usr/bin/env perl ########################################################### # Prepare a LaTeX run for two-way communication with Perl # # By Scott Pakin # ########################################################### #------------------------------------------------------------------- # This is file `perltex.pl', # generated with the docstrip utility. # # The original source files were: # # perltex.dtx (with options: `perltex') # # This is a generated file. # # Copyright (C) 2003-2024 Scott Pakin # # This file may be distributed and/or modified under the conditions # of the LaTeX Project Public License, either version 1.3c of this # license or (at your option) any later version. The latest # version of this license is in: # # http://www.latex-project.org/lppl.txt # # and version 1.3c or later is part of all distributions of LaTeX # version 2006/05/20 or later. #------------------------------------------------------------------- % \end{macrocode} % % \subsubsection{Top-level code evaluation} % % In previous versions of \perltex, the |--nosafe| option created and % ran code within a sandbox in which all operations are allowed (via % |Opcode::full_opset()|). Unfortunately, certain operations still fail % to work within such a sandbox. We therefore define a top-level % ``non-sandbox'', |top_level_eval()|, in which to execute code. % |top_level_eval()| merely calls |eval()| on its argument. However, it % needs to be declared top-level and before anything else because % |eval()| runs in the lexical scope of its caller. % \changes{v1.3}{2006/06/24}{Modified \perltex\ to eschew the sandbox % altogether when \texttt{--nosafe} is specified} % % \begin{macrocode} sub top_level_eval ($) { return eval $_[0]; } % \end{macrocode} % % \subsubsection{Perl modules and pragmas} % % We use |Safe| and |Opcode| to implement the secure sandbox, % |Getopt::Long| and |Pod::Usage| to parse the command line, and % various other modules and pragmas for miscellaneous things. % \begin{macrocode} use Safe; use Opcode; use Getopt::Long; use Pod::Usage; use File::Basename; use Fcntl; use POSIX; use File::Spec; use IO::Handle; use warnings; use strict; % \end{macrocode} % % \subsubsection{Variable declarations} % % With |use strict| in effect, we need to declare all of our variables. % For clarity, we separate our global-variable declarations into % variables corresponding to command-line options and other global % variables. % % \paragraph{Variables corresponding to command-line arguments} % \begin{perlvar}{$latexprog} % \begin{perlvar}{$runsafely} % \begin{perlvar}{@permittedops} % \begin{perlvar}{$usepipe} % |$latexprog| is the name of the \LaTeX{} executable % (e.g.,~``|latex|''). If |$runsafely| is~|1| (the default), then the % user's Perl code runs in a secure sandbox; if it's~|0|, then arbitrary % Perl code is allowed to run. |@permittedops| is a list of features % made available to the user's Perl code. Valid values are described in % Perl's |Opcode| manual page. \perltex's default is a list containing % only |:browse|. |$usepipe| is~|1| if \perltex\ should attempt to use % a named pipe for communicating with |latex| or~|0| if an ordinary file % should be used instead. % \begin{macrocode} my $latexprog; my $runsafely = 1; my @permittedops; my $usepipe = 1; % \end{macrocode} % \end{perlvar} % \end{perlvar} % \end{perlvar} % \end{perlvar} % % \paragraph{Filename variables} % \begin{perlvar}{$progname} % \begin{perlvar}{$jobname} % \begin{perlvar}{$toperl} % \begin{perlvar}{$fromperl} % \begin{perlvar}{$toflag} % \begin{perlvar}{$fromflag} % \begin{perlvar}{$doneflag} % \begin{perlvar}{$logfile} % \begin{perlvar}{$pipe} % |$progname| is the run-time name of the \perltex{} program. % |$jobname| is the base name of the user's |.tex| file, which defaults % to the \TeX{} default of |texput|. |$toperl| defines the filename % used for \LaTeX-to-Perl communication. |$fromperl| defines the % filename used for Perl-to-\LaTeX{} communication. |$toflag| is the % name of a file that will exist only after \LaTeX{} creates |$tofile|. % |$fromflag| is the name of a file that will exist only after Perl % creates |$fromfile|. |$doneflag| is the name of a file that will % exist only after Perl deletes |$fromflag|. |$logfile| is the name of % a log file to which \perltex{} writes verbose execution information. % |$pipe| is the name of a Unix named pipe (or ordinary file on % operating systems that lack support for persistent named pipes or in % the case that |$usepipe| is set to~|0|) used to convince the |latex| % process to yield control of the CPU\@. % \begin{macrocode} my $progname = basename $0; my $jobname = "texput"; my $toperl; my $fromperl; my $toflag; my $fromflag; my $doneflag; my $logfile; my $pipe; % \end{macrocode} % \end{perlvar} % \end{perlvar} % \end{perlvar} % \end{perlvar} % \end{perlvar} % \end{perlvar} % \end{perlvar} % \end{perlvar} % \end{perlvar} % % \paragraph{Other global variables} % \begin{perlvar}{@latexcmdline} % \begin{perlvar}{$styfile} % \begin{perlvar}{@macroexpansions} % \begin{perlvar}{$sandbox} % \begin{perlvar}{$sandbox_eval} % \begin{perlvar}{$latexpid} % |@latexcmdline| is the command line to pass to the \LaTeX{} % executable. |$styfile| is the string \noperlmac{} if \perltex{} is run % with \texttt{--makesty}, otherwise undefined. |@macroexpansions| is a % list of \PerlTeX{} macro expansions in the order they were encountered. % It is used for creating a \noperlmac{} file when \texttt{--makesty} is % specified. |$sandbox| is a secure sandbox in which to run code that % appeared in the \LaTeX{} document. |$sandbox_eval| is a subroutine % that evalutes a string within |$sandbox| (normally) or outside of all % sandboxes (if |--nosafe| is specified). |$latexpid| is the process~ID % of the |latex| process. % \begin{macrocode} my @latexcmdline; my $styfile; my @macroexpansions; my $sandbox = new Safe; my $sandbox_eval; my $latexpid; % \end{macrocode} % \end{perlvar} % \end{perlvar} % \end{perlvar} % \end{perlvar} % \end{perlvar} % \end{perlvar} % % \begin{perlvar}{$pipestring} % |$pipestring| is a constant string to write to the |$pipe| named pipe % (or file) at each \LaTeX{} synchronization point. Its particular % definition is really a bug workaround for \XeTeX\@. The current % version of \XeTeX{} reads the first few bytes of a file to determine % the character encoding (\mbox{UTF-8} or \mbox{UTF-16}, big-endian or % little-endian) then attempts to rewind the file pointer. Because % pipes can't be rewound, the effect is that the first two bytes of % |$pipe| are discarded and the rest are input. Hence, the % ``|\endinput|'' used in prior versions of \PerlTeX{} inserted a % spurious ``|ndinput|'' into the author's document. We therefore % define |$pipestring| such that it will not interfere with the document % even if the first few bytes are discarded. % \changes{v1.7}{2007/12/09}{Introduced this variable as a workaround for % \protect\XeTeX's attempt to rewind \texttt{\$pipe}} % \begin{macrocode} my $pipestring = "\%\%\%\%\% Generated by $progname\n\\endinput\n"; % \end{macrocode} % \end{perlvar} % % \subsubsection{Command-line conversion} % % In this section, \perltex{} parses its own command line and prepares a % command line to pass to |latex|. % % \paragraph{Parsing \perltex's command line} % We first set |$latexprog| to be the contents of the environment % variable |PERLTEX| or the value ``|latex|'' if |PERLTEX| is not % specified. We then use |Getopt::Long| to parse the command line, % leaving any parameters we don't recognize in the argument vector % (|@ARGV|) because these are presumably |latex| options. % \changes{v1.6}{2007/12/07}{Added an (undocumented) \texttt{--nopipe} % option to \protect\perltex\ to help it work with \protect\XeTeX} % \changes{v1.7}{2007/12/09}{Added an (undocumented) \texttt{--synctext} % option to alter the text written to \texttt{\$pipe}} % \begin{macrocode} $latexprog = $ENV{"PERLTEX"} || "latex"; Getopt::Long::Configure("require_order", "pass_through"); GetOptions("help" => sub {pod2usage(-verbose => 1)}, "latex=s" => \$latexprog, "safe!" => \$runsafely, % \end{macrocode} % The following two options are undocumented because the defaults should % always suffice. We're not yet removing these options, however, in % case they turn out to be useful for diagnostic purposes. % \begin{macrocode} "pipe!" => \$usepipe, "synctext=s" => \$pipestring, % \end{macrocode} % \begin{macrocode} "makesty" => sub {$styfile = "noperltex.sty"}, "permit=s" => \@permittedops) || pod2usage(2); % \end{macrocode} % % \paragraph{Preparing a \LaTeX{} command line} % \begin{perlvar}{$firstcmd} % \begin{perlvar}{$option} % We start by searching |@ARGV| for the first string that does not start % with ``|-|'' or ``|\|''. This string, which represents a filename, is % used to set |$jobname|. % \begin{macrocode} @latexcmdline = @ARGV; my $firstcmd = 0; for ($firstcmd=0; $firstcmd<=$#latexcmdline; $firstcmd++) { my $option = $latexcmdline[$firstcmd]; next if substr($option, 0, 1) eq "-"; if (substr ($option, 0, 1) ne "\\") { $jobname = basename $option, ".tex" ; $latexcmdline[$firstcmd] = "\\input $option"; } last; } push @latexcmdline, "" if $#latexcmdline==-1; % \end{macrocode} % \end{perlvar} % \end{perlvar} % % \begin{perlvar}{$separator} % To avoid conflicts with the code and parameters passed to Perl from % \LaTeX{} (see Figure~\vref{fig:tofile-define} and % Figure~\vref{fig:tofile-use}) we define a separator string, % |$separator|, containing 20 random uppercase letters. % \begin{macrocode} my $separator = ""; foreach (1 .. 20) { $separator .= chr(ord("A") + rand(26)); } % \end{macrocode} % \end{perlvar} % % Now that we have the name of the \LaTeX{} job (|$jobname|) we can % assign |$toperl|, |$fromperl|, |$toflag|, |$fromflag|, |$doneflag|, % |$logfile|, and |$pipe| in terms of |$jobname| plus a suitable % extension. % \begin{macrocode} $toperl = $jobname . ".topl"; $fromperl = $jobname . ".frpl"; $toflag = $jobname . ".tfpl"; $fromflag = $jobname . ".ffpl"; $doneflag = $jobname . ".dfpl"; $logfile = $jobname . ".lgpl"; $pipe = $jobname . ".pipe"; % \end{macrocode} % % We now replace the filename of the |.tex| file passed to \perltex{} % with a |\def|inition of the separator character, |\def|initions of the % various files, and the original file with |\input| prepended if % necessary. % \begin{macrocode} $latexcmdline[$firstcmd] = sprintf '\makeatletter' . '\def%s{%s}' x 7 . '\makeatother%s', '\plmac@tag', $separator, '\plmac@tofile', $toperl, '\plmac@fromfile', $fromperl, '\plmac@toflag', $toflag, '\plmac@fromflag', $fromflag, '\plmac@doneflag', $doneflag, '\plmac@pipe', $pipe, $latexcmdline[$firstcmd]; % \end{macrocode} % % \subsubsection{Increasing \PerlTeX's robustness} % % \changes{v2.0}{2009/11/24}{Refer to each communication file using % its absolute path. This makes \perltex\ robust to user code % that changes the current directory} % \changes{v2.1}{2010/07/10}{Replaced \texttt{abs\_path()} with % \texttt{File::Spec-\char"3Erel2abs()} because the latter seems % to be more robust to nonexistent files} % \begin{macrocode} $toperl = File::Spec->rel2abs($toperl); $fromperl = File::Spec->rel2abs($fromperl); $toflag = File::Spec->rel2abs($toflag); $fromflag = File::Spec->rel2abs($fromflag); $doneflag = File::Spec->rel2abs($doneflag); $logfile = File::Spec->rel2abs($logfile); $pipe = File::Spec->rel2abs($pipe); % \end{macrocode} % % \changes{v1.9}{2009/09/13}{Introduced handlers for \textsc{sigalrm} % and \textsc{sigpipe} to make \perltex\ more robust to \texttt{latex} % exiting at an inopportune time} % % \perltex\ may hang if |latex| exits right before the final pipe % communication. We therefore define a simple \signal{ALRM} handler % that lets \perltex\ exit after a given length of time has % elapsed.\label{code:sigalrm} % \begin{macrocode} $SIG{"ALRM"} = sub { undef $latexpid; exit 0; }; % \end{macrocode} % % To prevent Perl from aborting with a ``\texttt{Broken pipe}'' error % message if |latex| exits during the final pipe communication we tell % Perl to ignore \signal{PIPE} errors. |latex|'s exiting will be % caught via other means (the preceding \signal{ALRM} handler or the % following call to |waitpid|). % \begin{macrocode} $SIG{"PIPE"} = "IGNORE"; % \end{macrocode} % % \begin{perlsub}{delete_files} % On some operating systems and some filesystems, deleting a file may % not cause the file to disappear immediately. Because % \PerlTeX\ synchronizes Perl and \LaTeX\ via the filesystem it is % critical that file deletions be performed when requested. We % therefore define a |delete_files| subroutine that waits until each % file named in the argument list is truly deleted. % \changes{v1.9}{2009/09/13}{Replaced all % \texttt{unlink}\dots\texttt{while~-e} statements with calls to a new % \texttt{delete\_files} subroutine} % \begin{macrocode} sub delete_files (@) { foreach my $filename (@_) { unlink $filename; while (-e $filename) { unlink $filename; sleep 0; } } } % \end{macrocode} % \end{perlsub} % % \begin{perlsub}{awaitexists} % We define an |awaitexists| subroutine that waits for a given file to % exist. If |latex| exits while |awaitexists| is waiting, then % \perltex{} cleans up and exits, too. % \changes{v1.0a}{2003/08/06}{Bug fix: Added ``\texttt{undef % \$latexpid}'' to make the \texttt{END} block correctly return a % status code of~0 on success} % \changes{v1.9}{2009/09/13}{Hoisted \texttt{\$awaitexists} from the % main loop and made it a top-level subroutine} % \begin{macrocode} sub awaitexists ($) { while (!-e $_[0]) { sleep 0; if (waitpid($latexpid, &WNOHANG)==-1) { delete_files($toperl, $fromperl, $toflag, $fromflag, $doneflag, $pipe); undef $latexpid; exit 0; } } } % \end{macrocode} % \end{perlsub} % % \subsubsection{Launching \LaTeX} % % We start by deleting the |$toperl|, |$fromperl|, |$toflag|, % |$fromflag|, |$doneflag|, and |$pipe| files, in case any of these were % left over from a previous (aborted) run. We also create a log file % (|$logfile|), a named pipe (|$pipe|)---or a file containing only % |\endinput| if we can't create a named pipe---and, if |$styfile| is % defined, a \LaTeXe\ style file. As |@latexcmdline| contains the % complete command line to pass to |latex| we need only |fork| a new % process and have the child process overlay itself with |latex|. % \perltex{} continues running as the parent. % \begin{macrocode} delete_files($toperl, $fromperl, $toflag, $fromflag, $doneflag, $pipe); open (LOGFILE, ">$logfile") || die "open(\"$logfile\"): $!\n"; autoflush LOGFILE 1; if (defined $styfile) { open (STYFILE, ">$styfile") || die "open(\"$styfile\"): $!\n"; } % \end{macrocode} % \begin{macrocode} if (!$usepipe || !eval {mkfifo($pipe, 0600)}) { sysopen PIPE, $pipe, O_WRONLY|O_CREAT, 0755; autoflush PIPE 1; print PIPE $pipestring; close PIPE; $usepipe = 0; } % \end{macrocode} % \begin{macrocode} defined ($latexpid = fork) || die "fork: $!\n"; unshift @latexcmdline, $latexprog; if (!$latexpid) { exec {$latexcmdline[0]} @latexcmdline; die "exec('@latexcmdline'): $!\n"; } % \end{macrocode} % % \subsubsection{Preparing a sandbox} % % \perltex{} uses Perl's |Safe| and |Opcode| modules to declare a secure % sandbox (|$sandbox|) in which to run Perl code passed to it from % \LaTeX{}\@. When the sandbox compiles and executes Perl code, it % permits only operations that are deemed safe. For example, the Perl % code is allowed by default to assign variables, call functions, and % execute loops. However, it is not normally allowed to delete files, % kill processes, or invoke other programs. If \perltex\ is run with % the |--nosafe| option we bypass the sandbox entirely and execute Perl % code using an ordinary |eval()| statement. % \begin{macrocode} if ($runsafely) { @permittedops=(":browse") if $#permittedops==-1; $sandbox->permit_only (@permittedops); $sandbox_eval = sub {$sandbox->reval($_[0])}; } else { $sandbox_eval = \&top_level_eval; } % \end{macrocode} % % \subsubsection{Communicating with \LaTeX{}} % % The following code constitutes \perltex's main loop. Until |latex| % exits, the loop repeatedly reads Perl code from \LaTeX{}, evaluates % it, and returns the result as per the protocol described in % Figure~\vref{fig:comm-protocol}. % \changes{v1.0a}{2003/08/21}{Undefined \texttt{\string\string\string$/} % only locally} % % \begin{macrocode} while (1) { % \end{macrocode} % % \begin{perlvar}{$entirefile} % Wait for |$toflag| to exist. When it does, this implies that % |$toperl| must exist as well. We read the entire contents of % |$toperl| into the |$entirefile| variable and process it. % Figures~\ref{fig:tofile-define} and~\ref{fig:tofile-use} illustrate % the contents of |$toperl|. % \begin{macrocode} awaitexists($toflag); my $entirefile; { local $/ = undef; open (TOPERL, "<$toperl") || die "open($toperl): $!\n"; $entirefile = ; close TOPERL; } % \end{macrocode} % \end{perlvar} % % \begin{perlvar}{$optag} % \begin{perlvar}{$macroname} % \begin{perlvar}{@otherstuff} % We split the contents of |$entirefile| into an operation tag (either % |DEF|, |USE|, or |RUN|), the macro name, and everything else % (|@otherstuff|). If |$optag| is |DEF| then |@otherstuff| will contain % the Perl code to define. If |$optag| is |USE| then |@otherstuff| will % be a list of subroutine arguments. If |$optag| is |RUN| then % |@otherstuff| will be a block of Perl code to run. % \changes{v2.1}{2010/07/10}{Normalized line endings across % Unix\slash Windows\slash Macintosh} % \begin{macrocode} $entirefile =~ s/\r//g; my ($optag, $macroname, @otherstuff) = map {chomp; $_} split "$separator\n", $entirefile; % \end{macrocode} % \end{perlvar} % \end{perlvar} % \end{perlvar} % % We clean up the macro name by deleting all leading non-letters, % replacing all subsequent non-alphanumerics with ``|_|'', and % prepending ``|latex_|'' to the macro name. % \begin{macrocode} $macroname =~ s/^[^A-Za-z]+//; $macroname =~ s/\W/_/g; $macroname = "latex_" . $macroname; % \end{macrocode} % % If we're calling a subroutine, then we make the arguments more % palatable to Perl by single-quoting them and replacing every % occurrence of ``|\|'' with ``|\\|'' and every occurrence of ``|'|'' % with ``|\'|''. % \begin{macrocode} if ($optag eq "USE") { foreach (@otherstuff) { s/\\/\\\\/g; s/\'/\\\'/g; $_ = "'$_'"; } } % \end{macrocode} % % \begin{perlvar}{$perlcode} % There are three possible values that can be assigned to |$perlcode|. % If |$optag| is |DEF|, then |$perlcode| is made to contain a definition % of the user's subroutine, named |$macroname|. If |$optag| is |USE|, % then |$perlcode| becomes an invocation of |$macroname| which gets % passed all of the macro arguments. Finally, if |$optag| is |RUN|, % then |$perlcode| is the unmodified Perl code passed to us from % \perlmac. Figure~\ref{fig:latex-perl-define} presents an example % of how the following code converts a \PerlTeX{} macro definition into % a Perl subroutine definition and Figure~\ref{fig:latex-perl-use} % presents an example of how the following code converts a \PerlTeX{} % macro invocation into a Perl subroutine invocation. % % \begin{figure}[tbp] % \centering % \DeleteShortVerb\| % \begin{tabular}{l|l|} % \cline{2-2} % \LaTeX: & \verb|\perlnewcommand{\mymacro}[2]{%| \\ % & \verb| sprintf "Isn't $_[0] %s $_[1]?\n",| \\ % & \verb| $_[0]>=$_[1] ? ">=" : "<"| \\ % & \verb|}| \\ % \cline{2-2} % \multicolumn{2}{c}{} \\ % \multicolumn{1}{c}{} & \multicolumn{1}{c}{\Huge$\Downarrow$} \\ % \multicolumn{2}{c}{} \\ % \cline{2-2} % Perl: & \verb|sub latex_mymacro {| \\ % & \verb| sprintf "Isn't $_[0] %s $_[1]?\n",| \\ % & \verb| $_[0]>=$_[1] ? ">=" : "<"| \\ % & \verb|}| \\ % \cline{2-2} % \end{tabular} % \MakeShortVerb\| % \caption{Conversion from \LaTeX{} to Perl (subroutine definition)} % \label{fig:latex-perl-define} % \end{figure} % % \begin{figure}[tbp] % \centering % \DeleteShortVerb\| % \begin{tabular}{l|l|} % \cline{2-2} % \LaTeX: & \verb|\mymacro{12}{34}| \\ % \cline{2-2} % \multicolumn{2}{c}{} \\ % \multicolumn{1}{c}{} & \multicolumn{1}{c}{\Huge$\Downarrow$} \\ % \multicolumn{2}{c}{} \\ % \cline{2-2} % Perl: & \verb|latex_mymacro ('12', '34');| \\ % \cline{2-2} % \end{tabular} % \MakeShortVerb\| % \caption{Conversion from \LaTeX{} to Perl (subroutine invocation)} % \label{fig:latex-perl-use} % \end{figure} % % \begin{macrocode} my $perlcode; if ($optag eq "DEF") { $perlcode = sprintf "sub %s {%s}\n", $macroname, $otherstuff[0]; } elsif ($optag eq "USE") { $perlcode = sprintf "%s (%s);\n", $macroname, join(", ", @otherstuff); } elsif ($optag eq "RUN") { $perlcode = $otherstuff[0]; } else { die "${progname}: Internal error -- unexpected operation tag \"$optag\"\n"; } % \end{macrocode} % \end{perlvar} % % Log what we're about to evaluate. % \begin{macrocode} print LOGFILE "#" x 31, " PERL CODE ", "#" x 32, "\n"; print LOGFILE $perlcode, "\n"; % \end{macrocode} % % \begin{perlvar}{$result} % \begin{perlvar}{$msg} % We're now ready to execute the user's code using the |$sandbox_eval| % function. If a warning occurs we write it as a Perl comment to the % log file. If an error occurs (i.e.,~|$@| is defined) we replace the % result (|$result|) with a call to \LaTeXe's |\PackageError| macro to % return a suitable error message. We produce one error message for % sandbox policy violations (detected by the error message, |$@|, % containing the string ``\texttt{trapped by}'') and a different error % message for all other errors caused by executing the user's code. For % clarity of reading both warning and error messages, we elide the % string ``\texttt{at (eval} \meta{number}\texttt{) line} % \meta{number}''. Once |$result| is defined---as either the resulting % \LaTeX\ code or as a |\PackageError|---we store it in % |@macroexpansions| in preparation for writing it to \noperlmac\ % (when \perltex\ is run with \texttt{--makesty}). % \changes{v2.0}{2009/11/21}{Substituted % \texttt{\string\string\string\MessageBreak} for newline when % reporting error messages produced by user code} % \begin{macrocode} undef $_; my $result; { my $warningmsg; local $SIG{__WARN__} = sub {chomp ($warningmsg=$_[0]); return 0}; $result = $sandbox_eval->($perlcode); if (defined $warningmsg) { $warningmsg =~ s/at \(eval \d+\) line \d+\W+//; print LOGFILE "# ===> $warningmsg\n\n"; } } $result = "" if !$result || $optag eq "RUN"; if ($@) { my $msg = $@; $msg =~ s/at \(eval \d+\) line \d+\W+//; $msg =~ s/\n/\\MessageBreak\n/g; $msg =~ s/\s+/ /; $result = "\\PackageError{perltex}{$msg}"; my @helpstring; if ($msg =~ /\btrapped by\b/) { @helpstring = ("The preceding error message comes from Perl. Apparently,", "the Perl code you tried to execute attempted to perform an", "`unsafe' operation. If you trust the Perl code (e.g., if", "you wrote it) then you can invoke perltex with the --nosafe", "option to allow arbitrary Perl code to execute.", "Alternatively, you can selectively enable Perl features", "using perltex's --permit option. Don't do this if you don't", "trust the Perl code, however; malicious Perl code can do a", "world of harm to your computer system."); } else { @helpstring = ("The preceding error message comes from Perl. Apparently,", "there's a bug in your Perl code. You'll need to sort that", "out in your document and re-run perltex."); } my $helpstring = join ("\\MessageBreak\n", @helpstring); $helpstring =~ s/\. /.\\space\\space /g; $result .= "{$helpstring}"; } push @macroexpansions, $result if defined $styfile && $optag eq "USE"; % \end{macrocode} % \end{perlvar} % \end{perlvar} % % Log the resulting \LaTeX{} code. % \begin{macrocode} print LOGFILE "%" x 30, " LATEX RESULT ", "%" x 30, "\n"; print LOGFILE $result, "\n\n"; % \end{macrocode} % % We add |\endinput| to the generated \LaTeX{} code to suppress an % extraneous end-of-line character that \TeX{} would otherwise insert. % \begin{macrocode} $result .= '\endinput'; % \end{macrocode} % % Continuing the protocol described in Figure~\vref{fig:comm-protocol} % we now write |$result| (which contains either the result of executing % the user's or a |\PackageError|) to the |$fromperl| file, delete % |$toflag|, |$toperl|, and |$doneflag|, and notify \LaTeX{} by touching % the |$fromflag| file. As a performance optimization, we also write % |\endinput| into |$pipe| to wake up the |latex| process. % \begin{macrocode} open (FROMPERL, ">$fromperl") || die "open($fromperl): $!\n"; syswrite FROMPERL, $result; close FROMPERL; % \end{macrocode} % \begin{macrocode} delete_files($toflag, $toperl, $doneflag); % \end{macrocode} % \begin{macrocode} open (FROMFLAG, ">$fromflag") || die "open($fromflag): $!\n"; close FROMFLAG; % \end{macrocode} % \begin{macrocode} if (open (PIPE, ">$pipe")) { autoflush PIPE 1; print PIPE $pipestring; close PIPE; } % \end{macrocode} % % We have to perform one final \LaTeX-to-Perl synchronization step. % Otherwise, a subsequent |\perl|[|re|]|newcommand| would see that % |$fromflag| already exists and race ahead, finding that |$fromperl| % does not contain what it's supposed to. % \begin{macrocode} awaitexists($toperl); delete_files($fromflag); open (DONEFLAG, ">$doneflag") || die "open($doneflag): $!\n"; close DONEFLAG; % \end{macrocode} % Again, we awaken the |latex| process, which is blocked on |$pipe|. If % writing to the pipe takes more than one second we assume that |latex| % has exited and trigger the \signal{ALRM} handler % (page~\pageref{code:sigalrm}). % \begin{macrocode} alarm 1; if (open (PIPE, ">$pipe")) { autoflush PIPE 1; print PIPE $pipestring; close PIPE; } alarm 0; } % \end{macrocode} % % \subsubsection{Final cleanup} % % If we exit abnormally we should do our best to kill the child |latex| % process so that it doesn't continue running forever, holding onto % system resources. % \begin{macrocode} END { close LOGFILE; if (defined $latexpid) { kill (9, $latexpid); exit 1; } if (defined $styfile) { % \end{macrocode} % \changes{v1.4}{2007/09/29}{Added support for a \texttt{--makesty} % option that generates a \PerlTeX-free style file called % \texttt{noperltex.sty}} % This is the big moment for the \texttt{--makesty} option. We've % accumulated the output from each \PerlTeX\ macro invocation into % |@macroexpansions|, and now we need to produce a % \noperlmac\ file. We start by generating a boilerplate % header in which we set up the package and load both \pkgname{perltex} % and \pkgname{filecontents}. % \begin{macrocode} print STYFILE <<"STYFILEHEADER1"; \\NeedsTeXFormat{LaTeX2e}[1999/12/01] \\ProvidesPackage{noperltex} [2007/09/29 v1.4 Perl-free version of PerlTeX specific to $jobname.tex] STYFILEHEADER1 ; print STYFILE <<'STYFILEHEADER2'; \RequirePackage{filecontents} % Suppress the "Document must be compiled using perltex" error from perltex. \let\noperltex@PackageError=\PackageError \renewcommand{\PackageError}[3]{} \RequirePackage{perltex} \let\PackageError=\noperltex@PackageError % \end{macrocode} % \begin{macro}{\plmac@macro@invocation@num} % \begin{macro}{\plmac@show@placeholder} % \noperlmac\ works by redefining the |\plmac@show@placeholder| macro, % which normally outputs a framed ``\PerlTeX'' when \perltex\ isn't % running, changing it to input |noperltex-|\meta{number}|.tex| instead % (where \meta{number} is the contents of the % |\plmac@macro@invocation@num| counter). Each % |noperltex-|\meta{number}|.tex| file contains the output from a single % invocation of a \PerlTeX-defined macro. % \begin{macrocode} % Modify \plmac@show@placeholder to input the next noperltex-*.tex file % each time a PerlTeX-defined macro is invoked. \newcount\plmac@macro@invocation@num \gdef\plmac@show@placeholder#1#2\@empty{% \ifx#1U\relax \endgroup \advance\plmac@macro@invocation@num by 1\relax \global\plmac@macro@invocation@num=\plmac@macro@invocation@num \input{noperltex-\the\plmac@macro@invocation@num.tex}% \else \endgroup \fi } STYFILEHEADER2 ; % \end{macrocode} % \end{macro} % \end{macro} % % Finally, we need to have \noperlmac\ generate each of the % |noperltex-|\meta{number}|.tex| files. For each element of % |@macroexpansions| we use one |filecontents| environment to write the % macro expansion verbatim to a file. % \begin{macrocode} foreach my $e (0 .. $#macroexpansions) { print STYFILE "\n"; printf STYFILE "%% Invocation #%d\n", 1+$e; printf STYFILE "\\begin{filecontents}{noperltex-%d.tex}\n", 1+$e; print STYFILE $macroexpansions[$e], "\\endinput\n"; print STYFILE "\\end{filecontents}\n"; } print STYFILE "\\endinput\n"; close STYFILE; } exit 0; } __END__ % \end{macrocode} % % \subsubsection{\perltex{} POD documentation} % % \perltex{} includes documentation in Perl's POD (Plain Old % Documentation) format. This is used both to produce manual pages and % to provide usage information when \perltex{} is invoked with the % |--help| option. The POD documentation is not listed here as part of % the documented \perltex{} source code because it contains essentially % the same information as that shown in Section~\ref{sec:perltex-man}. % If you're curious what the POD source looks like then see the % generated \perltex{} file. % % \iffalse % \begin{macrocode} =head1 NAME perltex - enable LaTeX macros to be defined in terms of Perl code =head1 SYNOPSIS perltex [B<--help>] [B<--latex>=I] [B<-->[B]B] [B<--permit>=I] [B<--makesty>] [I] =head1 DESCRIPTION LaTeX -- through the underlying TeX typesetting system -- produces beautifully typeset documents but has a macro language that is difficult to program. In particular, support for complex string manipulation is largely lacking. Perl is a popular general-purpose programming language whose forte is string manipulation. However, it has no typesetting capabilities whatsoever. Clearly, Perl's programmability could complement LaTeX's typesetting strengths. B is the tool that enables a symbiosis between the two systems. All a user needs to do is compile a LaTeX document using B instead of B. (B is actually a wrapper for B, so no B functionality is lost.) If the document includes a C<\usepackage{perltex}> in its preamble, then C<\perlnewcommand> and C<\perlrenewcommand> macros will be made available. These behave just like LaTeX's C<\newcommand> and C<\renewcommand> except that the macro body contains Perl code instead of LaTeX code. =head1 OPTIONS B accepts the following command-line options: =over 4 =item B<--help> Display basic usage information. =item B<--latex>=I Specify a program to use instead of B. For example, C<--latex=pdflatex> would typeset the given document using B instead of ordinary B. =item B<-->[B]B Enable or disable sandboxing. With the default of B<--safe>, B executes the code from a C<\perlnewcommand> or C<\perlrenewcommand> macro within a protected environment that prohibits ``unsafe'' operations such as accessing files or executing external programs. Specifying B<--nosafe> gives the LaTeX document I to execute any arbitrary Perl code, including that which can harm the user's files. See L for more information. =item B<--permit>=I Permit particular Perl operations to be performed. The B<--permit> option, which can be specified more than once on the command line, enables finer-grained control over the B sandbox. See L for more information. =item B<--makesty> Generate a LaTeX style file called F. Replacing the document's C<\usepackage{perltex}> line with C<\usepackage{noperltex}> produces the same output but does not require PerlTeX, making the document suitable for distribution to people who do not have PerlTeX installed. The disadvantage is that F is specific to the document that produced it. Any changes to the document's PerlTeX macro definitions or macro invocations necessitates rerunning B with the B<--makesty> option. =back These options are then followed by whatever options are normally passed to B (or whatever program was specified with C<--latex>), including, for instance, the name of the F<.tex> file to compile. =head1 EXAMPLES In its simplest form, B is run just like B: perltex myfile.tex To use B instead of regular B, use the B<--latex> option: perltex --latex=pdflatex myfile.tex If LaTeX gives a ``C'' error and you trust the F<.tex> file you're trying to compile not to execute malicious Perl code (e.g., because you wrote it yourself), you can disable B's safety mechansisms with B<--nosafe>: perltex --nosafe myfile.tex The following command gives documents only B's default permissions (C<:browse>) plus the ability to open files and invoke the C