# How to 'robustly' use file contents in command arguments?

I have the following code which sets up a XeLaTeX document (to be run with --enable-write18) and defines two commands, \showme and \prRaise:

\documentclass[a4paper,oneside]{memoir}
\usepackage{polyglossia}
\setdefaultlanguage{english}
\usepackage{catchfile}

% ----------------------------------------------------------------------------------------------------------
\newcommand*{\showme}[1]{The value is #1.}

% ----------------------------------------------------------------------------------------------------------
\makeatletter%
\newdimen\pr@Height%

\newcommand{\prRaise}[2]{%
\setlength{\pr@Height}{\f@size pt}%
\raisebox{#1\pr@Height}{#2}%
}%

\makeatother%

% ----------------------------------------------------------------------------------------------------------

% ----------------------------------------------------------------------------------------------------------
\begin{document}

\showme{foobar}% => ”The value is foobar”.

\showme{\cxltxReadB}% => ”The value is -0.2 .”

\showme{\input{/tmp/CXLTXtempout}}% => ”The value is -0.2 .”

\showme{\immediate\input{/tmp/CXLTXtempout}}% => ”The value is -0.2 .”

A\prRaise{-0.2}{B}C% => "ABC" with B lowered

A\prRaise{-0.2 }{B}C% => "ABC" with B lowered (trailing space shouldn't be a problem)

A\prRaise{\input{/tmp/CXLTXtempout}}{B}C% !!! => Missing number, treated as zero.

A\prRaise{\immediate\input{/tmp/CXLTXtempout}}{B}% !!! => Missing number, treated as zero.

A\prRaise{\cxltxReadB}{B}C% !!! => Missing number, treated as zero.

\end{document}


The thing is, trying to use the file contents with \input, \immediate\input and \cxltxReadA ... \cxltxR works for both commands, but, crucially, all attempts to read the file contents within the argument to prRaise fail. It'd be great if it did work, because what i really want to do is call some external program to do a contextual computation, so i want to call that external program at exactly the point where i need it.

Why do the two commands behave so differently? What in the definition of \prRaise causes these troubles?

• Just for comparison. When you are using TeX (or XeTeX, but not LaTeX) then it is sufficient to write A\raise\input file.tex em\hbox{B}C instead of your \prRaise. LaTeX brings only complications. – wipet Nov 26 '14 at 19:23

As explained in Why is \input not expandable?, the LaTeX version of \input is not expandable. To use \input in the context you'd like to, as part of setting a length, you need everything to work by expansion. There are good reasons why LaTeX's \input is defined the way it is, but if you really need expansion use the saved TeX primitive:

\begin{filecontents*}{\jobname.tmp}
-0.2
\end{filecontents*}
\documentclass{article}
\makeatletter
\newlength\pr@Height
\newcommand\prRaise[2]{%
\setlength\pr@Height{\f@size pt}%
\raisebox{#1\pr@Height}{#2}%
}
\let\normalinput\@@input
\makeatother
\begin{document}
A\prRaise{\normalinput \jobname.tmp }{B}C
\end{document}


Note that the same issue applies to e.g.

A\prRaise{\cxltxReadB}{B}C%


as \cxltxReadB is trying to carry out a definition, which is again not expandable.

• What are those good reasons? I thought the only thing was making it to understand both “syntaxes”: TeX \input file and LaTeX \input{file}. – Manuel Nov 26 '14 at 17:51
• @Manuel Try using the primitive with an incorrect file name, then try the same with the LaTeX version. In particular, imagine you are doing this 20+ years ago on a session taking several minutes per page where 'bail out' with a incorrect name is not a good option and some form of graceful recover is better! (The 'look ahead' for { breaks expandability in any case.) – Joseph Wright Nov 26 '14 at 17:54
• @Manuel In case you miss it :-) What you'll find is that with the primitive you are basically stuck: either you have to be able to sort out the file name or kill the run. With the LaTeX version you can skip the loading entirely and just 'push on': as I say, important in the past. – Joseph Wright Nov 26 '14 at 17:58
• @Manuel The LaTeX version also knows about \input@path. – Joseph Wright Nov 26 '14 at 17:59
• @JosephWright "you have to be able to sort out the file name or kill the run" ... this is not exactly true. You can type "null" and go on. Because there exists an empty file null.tex in TeX distributions. – wipet Nov 26 '14 at 20:20

First of all let's get rid of \immediate, which makes sense only before \write, \openout and \closeout, because these operations are normally performed during shipout and with the \immediate prefix they are performed “immediately”.

So \immediate\input has just one effect, that of expanding \input until the first unexpandable token is found. Since this token is \let (resulting from \@ifnextchar), TeX ignores \immediate and moves on.

I would distinguish your calls of \prRaise between an “explicit” argument and a file to be read in.

\begin{filecontents*}{\jobname.tmp}
-0.2
\end{filecontents*}
\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand\prRaise{smm}
{
\dim_set:Nn \l_flow_pr_height_dim { \use:c { f@size } pt }
\IfBooleanTF{#1}
{% \tl_set_from_file:Nnn is the same as \CatchFileDef
\tl_set_from_file:Nnn \l_flow_pr_factor_tl { } { #2 }
}
{
\tl_set:Nn \l_flow_pr_factor_tl { #2 }
}
\flow_pr_raise:n { #3 }
}

\dim_new:N \l_flow_pr_height_dim
\tl_new:N \l_flow_pr_factor_tl

\cs_new_protected:Npn \flow_pr_raise:n #1
{
\raisebox{ \l_flow_pr_factor_tl \l_flow_pr_height_dim } { #1 }
}

\ExplSyntaxOff

\begin{document}
A\prRaise{-0.2}{B}C

A\prRaise*{\jobname.tmp}{B}C
\end{document}


• thanks for your effort... one more piece to demonstrate that doing TeX is more of an art than a science. this looks like black magic to me. – flow Nov 27 '14 at 12:26