summaryrefslogtreecommitdiff
path: root/macros/latex/required/tools/shellesc.dtx
blob: 46f2343b016cc6de508af9f7eac5df7d089af812 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
% \iffalse
%% Source File: shellesc.dtx
%% Copyright (C) 2015-2019
%%
%% The LaTeX3 Project and any individual authors listed elsewhere
%% in this file.
%%
%% This file may be distributed under the terms of the LPPL.
%% See README for details.
%
%<*dtx>
          \ProvidesFile{shellesc.dtx}
%</dtx>
%<package>\ifx\ProvidesPackage\undefined
%<package>\def\next#1#2[#3]{\wlog{#2 #3}}
%<package>\ifx\PackageInfo\undefined\def\PackageInfo#1#2{\wlog{#1: #2}}\fi
%<package>\ifx\PackageWarning\undefined\def\PackageWarning#1#2{\wlog{#1: #2}}\fi
%<package>\expandafter\next\fi
%<package>\ProvidesPackage{shellesc}
%<driver> \ProvidesFile{shellesc.drv}
% \fi
%         \ProvidesFile{shellesc.dtx}
       [2019/10/17 v1.0b unified shell escape interface for LaTeX]
%
% \iffalse
%<*driver>
\documentclass{ltxdoc}
\begin{document}
\DocInput{shellesc.dtx}
\end{document}
%</driver>
% \fi
%
% \GetFileInfo{shellesc.dtx}
%
% \title{The \textsf{shellesc} Package\thanks{This file
%        has version number \fileversion, last
%        revised \filedate.}}
% \author{\LaTeX3 project}
% \date{\filedate}
%
%
% \maketitle
%
% \vspace*{-\baselineskip}
% \enlargethispage{\baselineskip}
%
% \changes{v0.1b}{2016/02/02}{Doc typo fixes (JB)}
% \changes{v0.2a}{2016/06/07}{Improve use with plain TeX}
% \section{Introduction}
%
% 
% For many years web2c based \TeX\ implementations have used the syntax
% of the \verb|\write| command to access  system commands by using a
% special stream 18 (streams above 15 can not be allocated to files in
% classical \TeX\ so stream 18 would otherwise just print to the
% terminal).
%
% This is a useful extension that did not break the strict rules on
% extensions in classical \TeX.  This package provides a simple
% macro level interface hiding the \verb|write18| implementation
% so a command to remove a file on a unix-like system could be
% specified  using \verb|\ShellEscape{rm file.txt}| (or \verb|del| in
% windows). Note that by default system access is not allowed and
% \LaTeX\ will typically need to be called with the \verb|--shell-escape|
% command line option.
%
% The package may be used with standard \texttt{latex} or
% \texttt{pdflatex} or \texttt{xetex}, however it is mostly motivated by
% \texttt{lualatex} as from Lua\TeX~0.87 onwards Lua\TeX\ does \emph{not}
% support the \verb|\write18| syntax to access system commands: it has
% 256 write streams and stream 18 can be associated to a file and
% (without this package) has no special significance. This packge
% defines the same \verb|\ShellEscape| syntax in Lua\LaTeX, but the
% implementation is via Lua and the \verb|os.execute| function.
%
% \verb|\ShellEscape| in fact corresponds to \verb|\immediate\write18|
% (or \verb|\directlua|). Very rarely you may need to delay a system
% command until the current page is output (when page numbers are
% known) for this classically you could use \verb|\write18| (or
% (\verb|\latelua|). This package provides \verb|\DelayedShellEscape|
% as a common syntax for this use.
%
% The shell escape status may be queried by checking the integer (chardef)
% command \verb|\ShellEscapeStatus|, 0 (disabled) 1 (enabled) 2 (restricted).
%
% To aid porting existing documents to Lua\TeX~0.87 this package does
% overload the \verb|\write| command so that
% \verb|\write18{rm file.txt}|
% will work with Lua\TeX. Note that the redefinition of \verb|\write|
% can not detect whether \verb|\immediate| has been used,
% \verb|\immediate| will work as normal when writing to file streams
% or the terminal but the special case of stream 18 which is defined to
% use \verb|os.execute| always uses \verb|\directlua| (so corresponds
% to \verb|\immediate\write18|. In the rare situations that you need
% non-immediate \verb|\write18| in a document being ported to current
% Lua\TeX, you will need to change to use the
% \verb|\DelayedShellEscape| command.
% 
% \section{Implementation}
%
%    \begin{macrocode}
%<*package>
%    \end{macrocode}
%
% \changes{v1.0b}{2019/10/17}{Catcode protection}
%    \begin{macrocode}
\chardef\shellesc@quotecat\catcode`\"
\chardef\shellesc@underscorecat\catcode`\_
\@makeother\"
\@makeother\_
%    \end{macrocode}
%
% \subsection{Status Check}
%
%
% \subsection{The shellesc package interface}
%
% \begin{macro}{\ShellEscapeStatus}
% \changes{v1.0a}{2019/10/13}{Command Introduced}
% Integer value with meanings 0 (shell escape disabled), 1 (shell escape allowed), 2 (Restricted shell escape).
%
%    \begin{macrocode}
\chardef\ShellEscapeStatus
  \ifx\pdfshellescape\@undefined
    \ifx\shellescape\@undefined
      \ifx\directlua\@undefined
        \z@
      \else
        \directlua{%
          tex.sprint((status.shell_escape or os.execute()) .. " ")}
      \fi
    \else
      \shellescape
    \fi
  \else
    \pdfshellescape
  \fi
%    \end{macrocode}
% \end{macro}
%
% \changes{v0.2a}{2016/06/07}{spelling in messages}
%    \begin{macrocode}
\ifcase\ShellEscapeStatus
  \PackageWarning{shellesc}{Shell escape disabled}
\or
  \PackageInfo   {shellesc}{Unrestricted shell escape enabled}
\else
  \PackageInfo   {shellesc}{Restricted shell escape enabled}
\fi
%    \end{macrocode}
%
%
% \begin{macro}{\ShellEscape}
% \changes{v1.0a}{2019/10/13}{Lua logging for gh/195}
% Execute the supplied tokens as a system dependent command, assuming 
% such execution is allowed.
%    \begin{macrocode}
\ifx\lastsavedimageresourcepages\@undefined
  \protected\def\ShellEscape{\immediate\write18 }
%    \end{macrocode}
%
%    \begin{macrocode}
\else
  \protected\def\ShellEscape{\directlua\ShellEscape@Lua}
\fi
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\DelayedShellEscape}
% \changes{v0.1c}{2016/04/29}{Define \cs{DelayedShellEscape} not \cs{ShellEscape}(UF)}
% \changes{v1.0a}{2019/10/13}{Lua logging for gh/195}
% Execute the supplied tokens as a system dependent command, when this
% node is shipped out with the completed page, assuming 
% such execution is allowed.
%    \begin{macrocode}
\ifx\lastsavedimageresourcepages\@undefined
  \protected\def\DelayedShellEscape{\relax\write18 }
%    \end{macrocode}
%
%    \begin{macrocode}
\else
  \protected\def\DelayedShellEscape{\latelua\ShellEscape@Lua}
\fi
%    \end{macrocode}
% \end{macro}
%
%
%
% \begin{macro}{\ShellEscape@Lua}
% \changes{v1.0a}{2019/10/13}{loging for gh/195}
% Shared Lua code for \verb|\DelayedShellEscape| and \verb|\ShellEscape|.
%    \begin{macrocode}
\ifx\directlua\@undefined\else
\protected\def\ShellEscape@Lua#1{{%
local status, msg = os.execute("\luaescapestring{#1}")%
if status == nil then
    texio.write_nl("log",%
      "runsystem(" .. "\luaescapestring{#1}"%
       .. ")...(" .. msg .. ")\string\n")
  elseif status == 0 then
    texio.write_nl("log",%
      "runsystem(" .. "\luaescapestring{#1}"%
      .. ")...executed\string\n")
  else
    texio.write_nl("log",%
      "runsystem(" .. "\luaescapestring{#1}"%
       .. ")...failed " .. (msg or "") .. "\string\n")
  end
}}
\fi
%    \end{macrocode}
% \end{macro}
%
% \subsection{The write18 package interface}
%
% In web2c based engines other than Lua\TeX, |\write18| may be used
% directly.  The same was true in older LuaTeX, but from version 0.85
% onwards that is not available.
%
% The above |shellesc| package interface is recommended for new code,
% however for ease of porting existing documents and packages to newer
% Lua\TeX\ releases, a |\write18| interface is provided here via a
% call to Lua's |os.execute|.
%
% Note that as currently written this always does an \emph{immediate}
% call to the system.
%
% |\immediate| is supported but ignored, |\immediate\write18| and
% |\write18| both execute immediately. To use a delayed execution at
% the next shipout, use the |\DelayedShellEscape| command defined
% above.
%
% Note that it would be easy to make |\wriete18| defined here use
% delayed execution, just use |\DelayedShellEscape| instead of
% |ShellEscape| in the definition below. However detecting
% |\immediate| is tricky so the choice here is to always use the
% immediate form, which is overwhelmingly more commonly used with
% |\write18|.
%
% Stop at this point if not a recent Lua\TeX.
%    \begin{macrocode}
\ifx\lastsavedimageresourcepages\@undefined
 \catcode`\"\shellesc@quotecat
 \catcode`\_\shellesc@underscorecat
 \expandafter\endinput
\fi
%    \end{macrocode}
%
%    \begin{macrocode}
\directlua{%
%    \end{macrocode}
%
%    \begin{macrocode}
shellesc = shellesc or {}
%    \end{macrocode}
%
% Lua function to use the token scanner to grab the following \TeX\
% number, and then test if stream 18 is being used, and then insert an
% appropriate \TeX\ command to handle the following brace group in
% each case.
%    \begin{macrocode}
local function write_or_execute()
  local s = token.scan_int()
  if (s==18) then
     tex.sprint(\the\numexpr\catcodetable@atletter\relax,
                "\string\\ShellEscape ")
  else
     tex.sprint(\the\numexpr\catcodetable@atletter\relax,
                "\string\\shellesc@write " .. s)
  end
end
%    \end{macrocode}
%
%    \begin{macrocode}
shellesc.write_or_execute=write_or_execute
%    \end{macrocode}
%
%    \begin{macrocode}
}
%    \end{macrocode}
%
%    \begin{macrocode}
\let\shellesc@write\write
%    \end{macrocode}
%
%    \begin{macrocode}
\protected\def\write{\directlua{shellesc.write_or_execute()}}
%    \end{macrocode}
%
%    \begin{macrocode}
\catcode`\"\shellesc@quotecat
\catcode`\"\shellesc@underscorecat
%    \end{macrocode}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
% \Finale
%