1

Objective:

I am trying to key on on the presence of file content (comments are acceptable, I am trying to key in on content that would be displayed in the built PDF) to determine what path to take.

If a .tex file has displayable content, perform ActionA. If the .tex file is effectively empty (comments and blank lines only), perform ActionB.

What I have:

\documentclass[12pt]{book}
\usepackage{catchfile}
\usepackage{ifthen}

\begin{document}

\newcommand{\isFileEmpty}[1]{
  \setbox0=\hbox{#1\unskip}\ifdim\wd0=0pt%
    \boolean{true}%
  \else
    \boolean{false}%
  \fi
}

\CatchFileDef\filecontent{catchfile}{}%

Macro test line: \isFileEmpty\expandafter{\filecontent }

\ifthenelse{\isFileEmpty\expandafter{\filecontent }}%   <-- This is where the error occurs
{Do stuff with file}
{Do something else}

\end{document}



Sample my_tex_file.tex with content:


%% commented line
Actual text


Sample blank my_tex_file.tex:

%% Commented line


The error I see is:

<to be read again>
                  \setbox
l.106 ...ifthenelse{ \isFileEmpty{\filecontent }}

!  ==> Fatal error occured, no output PDF produced!

References:

I have been referencing https://archive.org/details/B-001-002-139/page/n383/mode/2up and https://www.overleaf.com/learn/latex/Articles/How_does_%5Cexpandafter_work:_An_introduction_to_TeX_tokens

What I'm at: I believe I can use additional \expandafters to get this to work, but I have been unsuccessful. I understand the basic concept of \expandafter but I lack the experience to get this particular use case to work.

Any help getting this to work woudl be greatly appreciated. Any alternatives to this method are also appreciated.

Thanks in advance for your help.

11
  • Your \isFileEmpty macro is going to take \expandafter as its argument. Try putting \expandafter before isFileEmpty as well. Also, since you have the contents of the file in \filecontent, you can use the methods here tex.stackexchange.com/questions/53068/… to check for contents. Your solution will blow up if, e.g., there's a paragraph break in the input file.
    – Don Hosek
    Jul 10, 2020 at 20:28
  • @DonaldHosek Well, a \par in restricted horizontal mode does nothing. However there are several other things that may go wrong if tried in restricted horizontal mode.
    – egreg
    Jul 11, 2020 at 9:19
  • What kind of contents do you expect in the files? Is it completely general or somehow restricted?
    – egreg
    Jul 11, 2020 at 9:28
  • What about files not coding visible things but macro definitions and the like? What about files not coding paragraphs of text of a document but coding floats? (Floats (figures/tables) are visible only in case of there being a page where they occur=in case of being processed via output-routine.) What about files coding overfull boxes of height/width/depth 0pt containing other boxes with visible material? What about files where braces denote begin/end of scopes and thus don't need to be balanced and therefore can disturb \CatchFileDef? Files with # can be a problem for \CatchFileDef, too. Jul 11, 2020 at 17:57
  • @DonaldHosek Thanks for inluding that link, I forgot to add that one. That is one of the links I used to create the existing existing \isFileEmpty definition. In isolation with the "Macro test line" above, the expected boolean is output.
    – rmrf
    Jul 13, 2020 at 13:37

1 Answer 1

1

The \isFileEmpty-test in your example seems to be intended to put into a box the things that got caught as a macro and to measure the width of that box and in case of that width being 0 to "assume" emptiness.

Such a test can be fooled/outmanoeuvred, e.g., by placing overfull boxes of depth/height/width 0pt containing displayable content:

\documentclass{article}
\usepackage{ifthen}
\usepackage{catchfile}

\begin{filecontents*}{filetocatch.tex}
\newbox\mybox
\setbox\mybox=\vbox{\hbox{Hello, world!}\hbox{Hello, world!}\hbox{Hello, world!}}%
\wd\mybox=0pt %
\ht\mybox=0pt %
\dp\mybox=0pt %
\box\mybox
\end{filecontents*}

\newboolean{IsFileEmpty}%
\newcommand{\isFileEmpty}[1]{%
  \setbox0=\hbox{#1\unskip}%
  \ifdim\wd0=0pt%
    \setboolean{IsFileEmpty}{true}%
  \else
    \setboolean{IsFileEmpty}{false}%
  \fi
  \ifthenelse{\boolean{IsFileEmpty}}%
}%

\parindent=0ex
\parskip=\baselineskip

\begin{document}
This is the result of the emptiness-test after catching the content
of the file \verb|filetocatch.tex| into the macro \verb|\filecontent|:

\CatchFileDef{\filecontent}{filetocatch.tex}{}%
\isFileEmpty{\filecontent}{File is empty!}{File is not empty!}

\bigskip

This is the result of carrying out the macro \verb|\filecontent|:

\filecontent

\end{document}

enter image description here


Floats might be a problem, too:

\documentclass{article}
\usepackage{ifthen}
\usepackage{catchfile}

\begin{filecontents*}{filetocatch.tex}
\begin{figure}[h]
This is a figure.
\caption{This is the figure's caption.}
\end{figure}
\end{filecontents*}

\newboolean{IsFileEmpty}%
\newcommand{\isFileEmpty}[1]{%
  \setbox0=\hbox{#1\unskip}%
  \ifdim\wd0=0pt%
    \setboolean{IsFileEmpty}{true}%
  \else
    \setboolean{IsFileEmpty}{false}%
  \fi
  \ifthenelse{\boolean{IsFileEmpty}}%
}%

\parindent=0ex
\parskip=\baselineskip

\begin{document}

\listoffigures

\CatchFileDef{\filecontent}{filetocatch.tex}{}%

This is the result of carrying out the macro \verb|\filecontent|:

\filecontent

\bigskip

Carrying out the emptiness-test after catching the content
of the file \verb|filetocatch.tex| into the macro \verb|\filecontent|
leads to an error-message on the terminal/console:

\verb|! LaTeX Error: Not in outer par mode.|

Subsequent error-messages follow.

Besides this you erroneously get as result:

\isFileEmpty{\filecontent}{File is empty!}{File is not empty!}

\end{document}

enter image description here


Verbatim-material (verbatim-environments / \verb|...|-commands) will also be a problem for the \isFileEmpty-test:

\documentclass{article}
\usepackage{ifthen}
\usepackage{catchfile}

\begin{filecontents*}{filetocatch.tex}
\begin{verbatim}
This is verbatim
\end{verbatim}
\end{filecontents*}

\newboolean{IsFileEmpty}%
\newcommand{\isFileEmpty}[1]{%
  \setbox0=\hbox{#1\unskip}%
  \ifdim\wd0=0pt%
    \setboolean{IsFileEmpty}{true}%
  \else
    \setboolean{IsFileEmpty}{false}%
  \fi
  \ifthenelse{\boolean{IsFileEmpty}}%
}%

\parindent=0ex
\parskip=\baselineskip

\begin{document}

\CatchFileDef{\filecontent}{filetocatch.tex}{}%

% Carrying out the emptiness-test after catching the content
% of the file filetocatch.tex into the macro \filecontent
% leads to error-messages on the terminal/console.
% 
% \verb|! LaTeX Error: Something's wrong--perhaps a missing \item.|
%
% Subsequent error-messages follow.
% 
% You get neither a result nor a proper output-file/.pdf-file 
% because LaTeX does not leave verbatim-mode and therefore 
% does not even find the \end{document}-directive.
%

\isFileEmpty{\filecontent}{File is empty!}{File is not empty!}

\end{document}

In case you wish to know whether some characters other than space-characters and other than newline-characters (usually Return / CR or CR+LF) and other than lines preceded by % are in the file, you can switch to verbatim-catcode-régime and switch the integer-parameter \endlinechar to a negative value by means of \CatchFileDef's third argument.

This would not be a test for displayable content. This would be a test for content that can be tokenized for further processing by TeX which consists of things other than space-characters/linebreaks/empty lines/comment-lines.

\documentclass{article}

\usepackage{catchfile}
\usepackage{listings} % Used only for displaying files
\lstset{numbers=left, numberstyle=\tiny, numbersep=5pt, 
        basicstyle=\ttfamily, language={}, frame=single}

\begin{filecontents*}{filetocatchA.tex}
This is some content.
\end{filecontents*}

\begin{filecontents*}{filetocatchB.tex}
% This is some comment



% This is some comment
  % 

\end{filecontents*}

\begin{filecontents*}{filetocatchC.tex}
\newbox\mybox
\setbox\mybox=\vbox{%
                     \hbox{Hello, world!}%
                     \hbox{Hello, world!}%
                     \hbox{Hello, world!}%
                   }%
\wd\mybox=0pt %
\ht\mybox=0pt %
\dp\mybox=0pt %
\box\mybox
\end{filecontents*}

\begin{filecontents*}{filetocatchD.tex}
\begin{figure}[h]
This is a figure.
\caption{This is the figure's caption.}
\end{figure}
\end{filecontents*}


\begin{filecontents*}{filetocatchE.tex}
{\def\empty{}\empty\empty\empty}%
\end{filecontents*}


\begin{filecontents*}{filetocatchF.tex}
}}}}{{{
Unbalanced braces.
\end{filecontents*}


\makeatletter
%%-----------------------------------------------------------------------------
%% Check whether argument contains no tokens/is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked contains no tokens/is
%%                       empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked contains some tokens/
%%                       is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%
%% The way in which this macro works is explained in my answer to the
%% TeX-LaTeX-StackExchange-Question "Expandable test for an empty token
%% list—methods, performance, and robustness" at
%% <https://tex.stackexchange.com/a/522506>
%% In that answer it is named \CheckWhetherEmpty.
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
  {\@firstoftwo\expandafter{} \@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument is blank (empty/no tokens at all or only
%% explicit space tokens (character code 32/category code 10)):
%%-----------------------------------------------------------------------------
%% -- Take advantage of the fact that TeX discards space tokens when
%%    "fetching" _un_delimited arguments: --
%% \UD@CheckWhetherBlank{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that
%%                        argument which is to be checked is blank>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked is not blank>}%
\newcommand\UD@CheckWhetherBlank[1]{%
  \romannumeral\expandafter\expandafter\expandafter\@secondoftwo
  \expandafter\UD@CheckWhetherNull\expandafter{\@firstoftwo#1{}.}%
}%

\newcommand{\isFileEmpty}[1]{%
  \begingroup
  \CatchFileDef{\filecontent}{#1}{%
    \let\do\@makeother
    \dospecials
    \catcode`\%=14 %
    \catcode`\ =10 %
    \catcode`\^^I=12 %
    \endlinechar=-1 %
  }%
  \expandafter\endgroup
  \expandafter\UD@CheckWhetherBlank
  \expandafter{\filecontent}%
}%
\makeatother

\parindent=0ex
\parskip=\baselineskip

\begin{document}

This is the content of \verb|filetocatchA.tex|:

\lstinputlisting{filetocatchA.tex}

This is the result of the empty-test:

\isFileEmpty{filetocatchA.tex}{File is empty!}{File is not empty!}

\leavevmode\hrule height .5\ht\strutbox\hfill

This is the content of \verb|filetocatchB.tex|:

\lstinputlisting{filetocatchB.tex}

This is the result of the empty-test:

\isFileEmpty{filetocatchB.tex}{File is empty!}{File is not empty!}

\leavevmode\hrule height .5\ht\strutbox\hfill

This is the content of \verb|filetocatchC.tex|:

\lstinputlisting{filetocatchC.tex}

This is the result of the empty-test:

\isFileEmpty{filetocatchC.tex}{File is empty!}{File is not empty!}

\leavevmode\hrule height .5\ht\strutbox\hfill

This is the content of \verb|filetocatchD.tex|:

\lstinputlisting{filetocatchD.tex}

This is the result of the empty-test:

\isFileEmpty{filetocatchD.tex}{File is empty!}{File is not empty!}

\leavevmode\hrule height .5\ht\strutbox\hfill

This is the content of \verb|filetocatchE.tex|:

\lstinputlisting{filetocatchE.tex}

This is the result of the empty-test:

\isFileEmpty{filetocatchE.tex}{File is empty!}{File is not empty!}

\leavevmode\hrule height .5\ht\strutbox\hfill

This is the content of \verb|filetocatchF.tex|:

\lstinputlisting{filetocatchF.tex}

This is the result of the empty-test:

\isFileEmpty{filetocatchF.tex}{File is empty!}{File is not empty!}

\end{document}

enter image description here

enter image description here

2
  • Thank you for the very thorough and well explained response. I lighty tested both and they are working wonderfully. I will implement verbatim-catcode-régime as that seems to cover every likely possibility that my use case would ever present, with the other option in reserve.
    – rmrf
    Jul 14, 2020 at 14:36
  • @rmrf I just added a link to my comment about \UD@CheckWhetherNull to the question Expandable test for an empty token list—methods, performance, and robustness Jul 14, 2020 at 14:46

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .