2

I put together a macro to write the contents of an external file to the log output. Unfortunately, I always read out the trailing \par that LaTeX adds when reading files. How do I test for this and remove it? I am interested in the "TeX way", but all answers are welcome, of course.

The example macro deals with the following conditions:

  • File is empty
  • File has contents

It does not cover the following condition:

  • File does not exist

In my example I do the following while compiling:

  1. write to a file called functions-\jobname.txt (collects every mention of \myfunction in body)
  2. read the file later functions-\jobname.txt
  3. use typeout to put the contents of this file at the end of the log output (used for collaborated content-related debugging)

Read to Log Macro

I use \typeout to write to the log and terminal. I probably need to add some logic to my \readtolog macro to test for the trailing \par, so I extracted it from my working example below.

\newread\customfile
\def\readtolog#1{%
  \openin\customfile=#1\relax
  \loop\unless\ifeof\file
    \read\customfile to\fileline
    \ifx\fileline\eolmarker
    \else
    \typeout{\fileline}
    \fi
  \repeat
  \closein\customfile
}

Example Code

\listfiles% used to illustrate atveryend which does not have much to do with the question
\documentclass{article}
\usepackage{fontspec}% compiled with xelatex
\usepackage{regexpatch}% use for \xapptocmd
\usepackage{etoolbox}% use for \tracingpatches
\usepackage{atveryend}% Add support for \AtEndAfterFileList

\newenvironment{logmessage}[1]{%
  \typeout{^^J**************************************************^^J%
  #1%
  ^^J************************************************** }}
  {\typeout{**************************************************}}

\def\myfunction#1{#1}% Macro appended to by \xapptocmd

\newread\customfile
\def\readtolog#1{% LOGIC NEEDED TO REMOVE TRAILING \par
  \openin\customfile=#1\relax
  \loop\unless\ifeof\customfile
    \read\customfile to\fileline
    \ifx\fileline\eolmarker
    \else
    \typeout{\fileline}
    \fi
  \repeat
  \closein\customfile
}

\def\checkfunctions{% Debug mode (enable writes and reads)
  \tracingpatches
  \newwrite\myfunctionsfile
  \AtBeginDocument{\immediate\openout\myfunctionsfile=functions-\jobname.txt}
  \AtEndDocument{\immediate\closeout\myfunctionsfile}
  \AtEndAfterFileList{\begin{logmessage}{Functions}\readtolog{functions-\jobname.txt}\end{logmessage}}
  \xapptocmd{\myfunction}{\immediate\write\myfunctionsfile{\unexpanded{##1}}}{}{}
}
\checkfunctions

\begin{document}
\myfunction{A function worth noting}
\end{document}

Log Output Snippet

What is the deal with \par? Is this related? How to trim the end of line character for each line read from an external file?

**************************************************
Functions
************************************************** 
A function worth noting 
\par 
**************************************************
 )
  • 1
    Not a solution, but a recommendation: \newread\file must go outside the definition of \readtolog. And I'd use a different name than \file. The same for \newwrite. – egreg May 31 '16 at 9:15
  • @egreg Thanks for the required recommendation :) Done. – Jonathan Komar May 31 '16 at 9:29
3

I'm not sure I understand the question but

\def\readtolog#1{% LOGIC NEEDED TO REMOVE TRAILING \par
  \openin\customfile=#1\relax
  {\endlinechar-1
  \loop\unless\ifeof\customfile
    \read\customfile to\fileline
    \typeout{\fileline}
  \repeat}%
  \closein\customfile
}

would suppress the special handling of two end of line turning into \par

If you want to trap an empty line at the end, just save each line, and output the previous line then just see at the end what you have, which will be an empty macro unless the last line of the file being read does not have an end of line.

\def\readtolog#1{% LOGIC NEEDED TO REMOVE TRAILING \par
  \openin\customfile=#1\relax
  {\endlinechar-1
  \def\temp{}%
  \loop\unless\ifeof\customfile
    \temp
    \read\customfile to\fileline
    \def\temp{\typeout{\fileline}}%
  \repeat
  \ifx\fileline\@empty\else\temp\fi% just in case there was not an eol on the last line
}%
  \closein\customfile
}
  • Really? What is not to understand in "How can I remove trailing \par when reading a file and writing the output to the log?" with the provided example output "A function worth noting \par" I am open to suggestions how to improve my question, but it seems clear to me. – Jonathan Komar Jun 1 '16 at 7:02
  • In hindsight, I can see why maybe you are unsure. The extra line is still present in the output, just not visible as \par. – Jonathan Komar Jun 1 '16 at 8:34
  • @macmadness86 yes if the blank line is a problem, could try a bit harder and remove it, but if a blank line is OK, easier to leave it there:-) – David Carlisle Jun 1 '16 at 8:54
  • Would you please point me in the right direction to deal with the blank lines? I am already getting questions about it from developers :) – Jonathan Komar Jun 10 '16 at 12:41
  • 1
    @macmadness86 like that? (I assume you just worry about the last line) if you never want a blank line at all do \ifx\fileline\@empty\else\typeout{\fileline}\fi in the original version – David Carlisle Jun 10 '16 at 13:16
2

A simple solution is to use catchfile:

\listfiles
\documentclass{article}
\usepackage{regexpatch}% use for \xapptocmd
\usepackage{atveryend}% Add support for \AtEndAfterFileList
\usepackage{catchfile}

\newenvironment{logmessage}[1]{%
  \typeout{^^J**************************************************^^J%
  #1%
  ^^J************************************************** }}
  {\typeout{**************************************************}}

\def\myfunction#1{#1}% Macro appended to by \xapptocmd

\long\def\readtolog#1{%
  \IfFileExists{#1}{%
    \CatchFileDef\tologfile{#1}{\endlinechar=`\^^J}%
    \typeout{\unexpanded\expandafter{\tologfile}}%
  }{\typeout{No file #1}}%
}

\newwrite\myfunctionsfile
\def\checkfunctions{% Debug mode (enable writes and reads)
  \AtBeginDocument{\immediate\openout\myfunctionsfile=functions-\jobname.txt}%
  \AtEndDocument{\immediate\closeout\myfunctionsfile}%
  \AtEndAfterFileList{%
    \begin{logmessage}{Functions}
    \readtolog{functions-\jobname.txt}
    \end{logmessage}%
  }%
  \xapptocmd{\myfunction}
    {\immediate\write\myfunctionsfile{\unexpanded{##1} -- line \the\inputlineno}}
    {}{}%
}
\checkfunctions

\begin{document}
\myfunction{A function worth noting}
\myfunction{A function worth noting again}
\end{document}

This produces a blank line before the closing asterisks, but I can't see it as a real problem.

 *File List*
 article.cls    2014/09/29 v1.4h Standard LaTeX document class
  size10.clo    2014/09/29 v1.4h Standard LaTeX file (size option)
regexpatch.sty    2015/05/20 v0.2a Extending etoolbox patching commands
   expl3.sty    2016/03/28 v6468 L3 programming layer (loader) 
expl3-code.tex    2016/03/28 v6468 L3 programming layer 
l3pdfmode.def    2016/03/26 v6465 L3 Experimental driver: PDF mode
  xparse.sty    2016/03/28 v6468 L3 Experimental document command parser
 l3regex.sty    2016/03/26 v6466 L3 Experimental regular expressions
l3tl-build.sty    2016/03/26 v6466 L3 Experimental token list construction
l3tl-analysis.sty    2016/03/24 v6443 L3 Experimental token lists analysis
  l3flag.sty    2016/03/26 v6466 L3 Experimental flags
l3str-convert.sty    2016/03/24 v6443 L3 Experimental string encoding conversio
ns
atveryend.sty    2011/06/30 v1.8 Hooks at the very end of document (HO)
catchfile.sty    2011/03/01 v1.6 Catch the contents of a file (HO)
infwarerr.sty    2010/04/08 v1.3 Providing info/warning/error messages (HO)
 ltxcmds.sty    2011/11/09 v1.22 LaTeX kernel commands for general use (HO)
etexcmds.sty    2011/02/16 v1.5 Avoid name clashes with e-TeX commands (HO)
ifluatex.sty    2010/03/01 v1.3 Provides the ifluatex switch (HO)
 ***********


**************************************************
Functions
************************************************** 
(./functions-readtolog.txt)
A function worth noting -- line 38
A function worth noting again -- line 39

**************************************************

A very simple routine for reading a file line by line without the final empty line problem is obtained with expl3. Just redefine your \readtolog macro in the following way:

\ExplSyntaxOn
\ior_new:N \g_macmadness_logfile_stream
\cs_new_protected:Npn \readtolog #1
 {
  \file_if_exist:nTF { #1 }
   {
    \macmadness_logfile_typeout:n { #1 }
   }
   {
    \typeout{No~file~#1}
   }
 }
\cs_new_protected:Nn \macmadness_logfile_typeout:n
 {
  \ior_open:Nn \g_macmadness_logfile_stream { #1 }
  \ior_map_inline:Nn \g_macmadness_logfile_stream
   {
    \typeout{ \exp_not:n { ##1 } }
   }
 }
\ExplSyntaxOff

Full example:

\listfiles
\documentclass{article}
\usepackage{regexpatch}% use for \xapptocmd; already loads expl3
\usepackage{atveryend}% Add support for \AtEndAfterFileList

\ExplSyntaxOn
\ior_new:N \g_macmadness_logfile_stream
\cs_new_protected:Npn \readtolog #1
 {
  \file_if_exist:nTF { #1 }
   {
    \macmadness_logfile_typeout:n { #1 }
   }
   {
    \typeout{No~file~#1}
   }
 }
\cs_new_protected:Nn \macmadness_logfile_typeout:n
 {
  \ior_open:Nn \g_macmadness_logfile_stream { #1 }
  \ior_map_inline:Nn \g_macmadness_logfile_stream
   {
    \typeout{ \exp_not:n { ##1 } }
   }
 }
\ExplSyntaxOff

\newenvironment{logmessage}[1]{%
  \typeout{^^J**************************************************^^J%
  #1%
  ^^J************************************************** }}
  {\typeout{**************************************************}}

\def\myfunction#1{#1}% Macro appended to by \xapptocmd

\newwrite\myfunctionsfile
\def\checkfunctions{% Debug mode (enable writes and reads)
  \AtBeginDocument{\immediate\openout\myfunctionsfile=functions-\jobname.txt}%
  \AtEndDocument{\immediate\closeout\myfunctionsfile}%
  \AtEndAfterFileList{%
    \begin{logmessage}{Functions}
    \readtolog{functions-\jobname.txt}
    \end{logmessage}%
  }%
  \xapptocmd{\myfunction}
    {\immediate\write\myfunctionsfile{\unexpanded{##1} -- line \the\inputlineno}}
    {}{}%
}
\checkfunctions

\begin{document}
\myfunction{A function worth noting}
\myfunction{A function worth noting again}
\end{document}

Output:

 *File List*
 article.cls    2014/09/29 v1.4h Standard LaTeX document class
  size10.clo    2014/09/29 v1.4h Standard LaTeX file (size option)
regexpatch.sty    2015/05/20 v0.2a Extending etoolbox patching commands
   expl3.sty    2016/03/28 v6468 L3 programming layer (loader) 
expl3-code.tex    2016/03/28 v6468 L3 programming layer 
l3pdfmode.def    2016/03/26 v6465 L3 Experimental driver: PDF mode
  xparse.sty    2016/03/28 v6468 L3 Experimental document command parser
 l3regex.sty    2016/03/26 v6466 L3 Experimental regular expressions
l3tl-build.sty    2016/03/26 v6466 L3 Experimental token list construction
l3tl-analysis.sty    2016/03/24 v6443 L3 Experimental token lists analysis
  l3flag.sty    2016/03/26 v6466 L3 Experimental flags
l3str-convert.sty    2016/03/24 v6443 L3 Experimental string encoding conversio
ns
atveryend.sty    2011/06/30 v1.8 Hooks at the very end of document (HO)
 ***********


**************************************************
Functions
************************************************** 
A function worth noting -- line 52 
A function worth noting again -- line 53 
**************************************************
  • What is causing the \customfile=\read<number> in the file output. It does not appear in the terminal output (no read file handle opened). In yours I noticed you have (./functions-readtolog.txt) – Jonathan Komar Jun 2 '16 at 13:12
  • @macmadness86 When you do \newread, an entry in the log file is always inserted; on the other hand \CatchFileDef does \input, so the file name is shown in the log file as usual. – egreg Jun 2 '16 at 16:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.