From e0c6872cf40896c7be36b11dcc744620f10adf1d Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Mon, 2 Sep 2019 13:46:59 +0900 Subject: Initial commit --- macros/latex/contrib/perltex/README | 55 + macros/latex/contrib/perltex/perltex.1 | 280 +++ macros/latex/contrib/perltex/perltex.dtx | 3207 ++++++++++++++++++++++++++++++ macros/latex/contrib/perltex/perltex.ins | 68 + macros/latex/contrib/perltex/perltex.pdf | Bin 0 -> 333786 bytes 5 files changed, 3610 insertions(+) create mode 100644 macros/latex/contrib/perltex/README create mode 100644 macros/latex/contrib/perltex/perltex.1 create mode 100644 macros/latex/contrib/perltex/perltex.dtx create mode 100644 macros/latex/contrib/perltex/perltex.ins create mode 100644 macros/latex/contrib/perltex/perltex.pdf (limited to 'macros/latex/contrib/perltex') diff --git a/macros/latex/contrib/perltex/README b/macros/latex/contrib/perltex/README new file mode 100644 index 0000000000..cf6fcdcb5e --- /dev/null +++ b/macros/latex/contrib/perltex/README @@ -0,0 +1,55 @@ + +------------------------------------+ + | PERLTEX | + | By Scott Pakin, scott+pt@pakin.org | + +------------------------------------+ + + +Description +----------- + +PerlTeX is a combination Perl script (perltex.pl) and LaTeX2e style +file (perltex.sty) 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. + + +Installation +------------ + +In case it's not obvious, you'll need a Perl installation. Perl is +freely available and runs on dozens of platforms. See the list of Perl +binary distributions on CPAN (http://www.cpan.org/ports/index.html). + +Execute "latex perltex.ins" to generate perltex.sty and perltex.pl. +Install perltex.sty in an appropriate directory (e.g., +/usr/local/share/texmf/tex/latex/misc or C:\localtexmf\tex\latex\misc). +perltex.pl is a Perl script. On Unix, you may want to rename it to +just "perltex" and set execute permissions ("chmod +x perltex"). On +Windows, you may want to create a batch-file wrapper for it ("pl2bat +perltex.pl"). In either case, perltex should be installed somewhere in +your executable path (e.g., /usr/local/bin). + +It's not necessary to produce the documentation as PerlTeX comes with +prebuilt documentation in PDF format (perltex.pdf). However, if you +want to build the documentation yourself (e.g., to use A4 paper instead +of U.S. Letter), run "latex perltex.dtx" twice, then "makeindex -s +gind.ist perltex.idx" and "makeindex -s gglo.ist perltex.glo -o +perltex.gls", then "latex perltex.dtx" again, then "dvips perltex.dvi +-o perltex.ps" if you want to produce PostScript. + + +License +------- + +Copyright (C) 2010 Scott Pakin + +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: + + 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. diff --git a/macros/latex/contrib/perltex/perltex.1 b/macros/latex/contrib/perltex/perltex.1 new file mode 100644 index 0000000000..28c0508e68 --- /dev/null +++ b/macros/latex/contrib/perltex/perltex.1 @@ -0,0 +1,280 @@ +.\" Automatically generated by Pod::Man 2.1801 (Pod::Simple 3.05) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is turned on, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.ie \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. nr % 0 +. rr F +.\} +.el \{\ +. de IX +.. +.\} +.\" +.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). +.\" Fear. Run. Save yourself. No user-serviceable parts. +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds / +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +.\} +.rm #[ #] #H #V #F C +.\" ======================================================================== +.\" +.IX Title "PERLTEX 1" +.TH PERLTEX 1 "2010-07-10" "v2.1" " " +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +perltex \- enable LaTeX macros to be defined in terms of Perl code +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +perltex +[\fB\-\-help\fR] +[\fB\-\-latex\fR=\fIprogram\fR] +[\fB\-\-\fR[\fBno\fR]\fBsafe\fR] +[\fB\-\-permit\fR=\fIfeature\fR] +[\fB\-\-makesty\fR] +[\fIlatex options\fR] +.SH "DESCRIPTION" +.IX Header "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. +.PP +Clearly, Perl's programmability could complement LaTeX's typesetting +strengths. \fBperltex\fR is the tool that enables a symbiosis between +the two systems. All a user needs to do is compile a LaTeX document +using \fBperltex\fR instead of \fBlatex\fR. (\fBperltex\fR is actually a +wrapper for \fBlatex\fR, so no \fBlatex\fR functionality is lost.) If the +document includes a \f(CW\*(C`\eusepackage{perltex}\*(C'\fR in its preamble, then +\&\f(CW\*(C`\eperlnewcommand\*(C'\fR and \f(CW\*(C`\eperlrenewcommand\*(C'\fR macros will be made +available. These behave just like LaTeX's \f(CW\*(C`\enewcommand\*(C'\fR and +\&\f(CW\*(C`\erenewcommand\*(C'\fR except that the macro body contains Perl code instead +of LaTeX code. +.SH "OPTIONS" +.IX Header "OPTIONS" +\&\fBperltex\fR accepts the following command-line options: +.IP "\fB\-\-help\fR" 4 +.IX Item "--help" +Display basic usage information. +.IP "\fB\-\-latex\fR=\fIprogram\fR" 4 +.IX Item "--latex=program" +Specify a program to use instead of \fBlatex\fR. For example, +\&\f(CW\*(C`\-\-latex=pdflatex\*(C'\fR would typeset the given document using +\&\fBpdflatex\fR instead of ordinary \fBlatex\fR. +.IP "\fB\-\-\fR[\fBno\fR]\fBsafe\fR" 4 +.IX Item "--[no]safe" +Enable or disable sandboxing. With the default of \fB\-\-safe\fR, +\&\fBperltex\fR executes the code from a \f(CW\*(C`\eperlnewcommand\*(C'\fR or +\&\f(CW\*(C`\eperlrenewcommand\*(C'\fR macro within a protected environment that +prohibits ``unsafe'' operations such as accessing files or executing +external programs. Specifying \fB\-\-nosafe\fR gives the LaTeX document +\&\fIcarte blanche\fR to execute any arbitrary Perl code, including that +which can harm the user's files. See Safe for more information. +.IP "\fB\-\-permit\fR=\fIfeature\fR" 4 +.IX Item "--permit=feature" +Permit particular Perl operations to be performed. The \fB\-\-permit\fR +option, which can be specified more than once on the command line, +enables finer-grained control over the \fBperltex\fR sandbox. See +Opcode for more information. +.IP "\fB\-\-makesty\fR" 4 +.IX Item "--makesty" +Generate a LaTeX style file called \fInoperltex.sty\fR. Replacing the +document's \f(CW\*(C`\eusepackage{perltex}\*(C'\fR line with \f(CW\*(C`\eusepackage{noperltex}\*(C'\fR +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 \fInoperltex.sty\fR is specific to +the document that produced it. Any changes to the document's PerlTeX +macro definitions or macro invocations necessitates rerunning +\&\fBperltex\fR with the \fB\-\-makesty\fR option. +.PP +These options are then followed by whatever options are normally +passed to \fBlatex\fR (or whatever program was specified with +\&\f(CW\*(C`\-\-latex\*(C'\fR), including, for instance, the name of the \fI.tex\fR file to +compile. +.SH "EXAMPLES" +.IX Header "EXAMPLES" +In its simplest form, \fBperltex\fR is run just like \fBlatex\fR: +.PP +.Vb 1 +\& perltex myfile.tex +.Ve +.PP +To use \fBpdflatex\fR instead of regular \fBlatex\fR, use the \fB\-\-latex\fR +option: +.PP +.Vb 1 +\& perltex \-\-latex=pdflatex myfile.tex +.Ve +.PP +If LaTeX gives a ``\f(CW\*(C`trapped by operation mask\*(C'\fR'' error and you trust +the \fI.tex\fR file you're trying to compile not to execute malicious +Perl code (e.g., because you wrote it yourself), you can disable +\&\fBperltex\fR's safety mechansisms with \fB\-\-nosafe\fR: +.PP +.Vb 1 +\& perltex \-\-nosafe myfile.tex +.Ve +.PP +The following command gives documents only \fBperltex\fR's default +permissions (\f(CW\*(C`:browse\*(C'\fR) plus the ability to open files and invoke the +\&\f(CW\*(C`time\*(C'\fR command: +.PP +.Vb 2 +\& perltex \-\-permit=:browse \-\-permit=:filesys_open +\& \-\-permit=time myfile.tex +.Ve +.SH "ENVIRONMENT" +.IX Header "ENVIRONMENT" +\&\fBperltex\fR honors the following environment variables: +.IP "\s-1PERLTEX\s0" 4 +.IX Item "PERLTEX" +Specify the filename of the LaTeX compiler. The LaTeX compiler +defaults to ``\f(CW\*(C`latex\*(C'\fR''. The \f(CW\*(C`PERLTEX\*(C'\fR environment variable +overrides this default, and the \fB\-\-latex\fR command-line option (see +\&\*(L"\s-1OPTIONS\s0\*(R") overrides that. +.SH "FILES" +.IX Header "FILES" +While compiling \fIjobname.tex\fR, \fBperltex\fR makes use of the following +files: +.IP "\fIjobname.lgpl\fR" 4 +.IX Item "jobname.lgpl" +log file written by Perl; helpful for debugging Perl macros +.IP "\fIjobname.topl\fR" 4 +.IX Item "jobname.topl" +information sent from LaTeX to Perl +.IP "\fIjobname.frpl\fR" 4 +.IX Item "jobname.frpl" +information sent from Perl to LaTeX +.IP "\fIjobname.tfpl\fR" 4 +.IX Item "jobname.tfpl" +``flag'' file whose existence indicates that \fIjobname.topl\fR contains +valid data +.IP "\fIjobname.ffpl\fR" 4 +.IX Item "jobname.ffpl" +``flag'' file whose existence indicates that \fIjobname.frpl\fR contains +valid data +.IP "\fIjobname.dfpl\fR" 4 +.IX Item "jobname.dfpl" +``flag'' file whose existence indicates that \fIjobname.ffpl\fR has been +deleted +.IP "\fInoperltex\-#.tex\fR" 4 +.IX Item "noperltex-#.tex" +file generated by \fInoperltex.sty\fR for each PerlTeX macro invocation +.SH "NOTES" +.IX Header "NOTES" +\&\fBperltex\fR's sandbox defaults to what Opcode calls ``\f(CW\*(C`:browse\*(C'\fR''. +.SH "SEE ALSO" +.IX Header "SEE ALSO" +\&\fIlatex\fR\|(1), \fIpdflatex\fR\|(1), \fIperl\fR\|(1), \fISafe\fR\|(3pm), \fIOpcode\fR\|(3pm) +.SH "AUTHOR" +.IX Header "AUTHOR" +Scott Pakin, \fIscott+pt@pakin.org\fR diff --git a/macros/latex/contrib/perltex/perltex.dtx b/macros/latex/contrib/perltex/perltex.dtx new file mode 100644 index 0000000000..3f61570dda --- /dev/null +++ b/macros/latex/contrib/perltex/perltex.dtx @@ -0,0 +1,3207 @@ +% \iffalse meta-comment +% +% Copyright (C) 2010 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> + [2010/07/10 v2.1 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} +\IfFileExists{hyperref.sty}{% + \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{602} +% +% \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{} 2010 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; and to Linus K\"allberg for reporting and helping diagnose +% some problems with running \PerlTeX\ on Windows. 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|. +% \begin{macrocode} +\newif\ifperl +\perltrue +\@ifundefined{plmac@tag}{\perlfalse}{} +\@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@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 |\plmac@await@existence| macro is to repeatedly +% check the existence of a given file until the file actually exists. +% For convenience, we use \LaTeXe's |\IfFileExists| macro to check the +% file and invoke |\plmac@file@existstrue| or |\plmac@file@existsfalse|, +% as appropriate. +% +% 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 + \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) 2010 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