tex-core – Test Success of write18

conditionalsshell-escapetex-core

For the new version of my package and class standalone I like to call external programs: further (pdf)latex runs to optionally compile included standalone files, and to convert the resulting PDF to PNG (depends on the fact that the PDF created by the last run isn't overwritten at least until \begin{document} or until the first page is flushed out).

I know of course this can be done using \immediate\write18{<command> <arguments>} which requires the command to be either added to the shell_escape_commands variable of texmf.cnf and shell_escape to be set to p or that the user uses the -shell-escape argument.

Now I like to test in my code if the write18 call was successfully, i.e. if it was allowed to be executed. I know that \ifeof18 will return false if the write18 feature is completely disabled (shell_escape = 0 in texmf.cnf or when the -no-shell-escape argument is used), but it returns true if the restricted write18 mode is activated which is the default.

Is there a way to test if the last write18 call was blocked? (I know that I can test manually if the file which should be produced by the write18 call was created or updated.)


Update

In the .log file there its one of the following lines displayed for every write18 call, depending if this feature is disabled, enabled or running in restricted mode with the command allowed or not, respectively:

runsystem(<command>)...disabled.
runsystem(<command>)...executed.
runsystem(<command>)...executed safely (allowed).
runsystem(<command>)...disabled (restricted).

So how can I get this information inside the LaTeX file (without actually reading the .log file in)?

Best Answer

It seems that pdflatex does not provide any other status variables beyond \ifeof18 and \pgfshellescape. This got confirmed on c.t.t by Heiko Oberdiek. He also recommended to open a feature request for pdfTeX (and/or other TeX engines), which I did.

Here the solution I came up with:

As mentioned in the two answers one good way to check for write18 is the \pdfshellescape macro, available since pdftex 1.30.0. It is 1 if \write18 is enabled, 2 if it is restricted, and 0 otherwise. It is not available in XeTeX or LuaTeX. However, the package pdftexcmds by Heiko Oberdiek adds it to LuaTeX and also checks for pdfTeX >=1.30.0 for you. The macro is then provided as \pdf@shellescape.

In my case I want to check if a conversion command was successful. I had the idea to use \pdffilemoddate (\pdf@filemoddate with pdftexcmds) to check if the to be generated file is newer afterwards or not. Together with \IfFileExists this allows for a very reasonable test which works with pdfLaTeX and LuaLaTeX. For XeLaTeX only a very limited test can be implemented.

The following macro awaits the name of the output file and the command:

 \writesuccess{<output file>}{<command line>}{<success>}{<failure>}

However, if the called command is executed but fails and updates the file modification date in the process the macro would report it as success. There is no way at the moment to check for this.

The full code with an example: conversion of the resulting PDF to PNG, needs two runs, where at least the second one requires -shell-escape:

\documentclass{standalone}

% Use `pdftexcmds` to support LuaTeX and take advantage of
% the included checks like version of pdflatex, existence of \ifeof18, etc.:
\usepackage{pdftexcmds}
\makeatletter
\ifx\pdf@filemoddate\@undefined

% Without filemoddate (e.g. XeLaTeX) only the existence of the file can be tested:
\newcommand{\writesuccess}[2]{%
    \immediate\write18{#2}%
    \IfFileExists{#1}%
}

\else

% Real version:
% Check if to-be-generated file exists afterwards and is newer:
\newcommand{\writesuccess}[2]{%
    \begingroup
    \edef\filemodbefore{\pdf@filemoddate{#1}}%
    \immediate\write18{#2}%
    \IfFileExists{#1}{%
        \edef\filemodafter{\pdf@filemoddate{#1}}%
        \ifx\filemodbefore\filemodafter
            \let\next\@secondoftwo
        \else
            \let\next\@firstoftwo
        \fi
    }{%
        \let\next\@secondoftwo
    }%
    \expandafter
    \endgroup
    \next
}

\fi
\makeatother


\writesuccess{\jobname.png}{convert -density 600 \jobname.pdf \jobname.png}{%
    \message{^^JYes^^J}%
}{%
    \message{^^JNo^^J}%
}

\begin{document}
Test
\end{document}