4

I am experimenting a little bit with automatic generation of file handles via \newwrite.

I want to achieve following behaviour:

  • A user provides a list of file categories, for example Hints, Solutions, Problems etc. and a list of corresponding extensions, say .hints, .sol, .prb in the correct order (how should TeX know which category belongs to which extension and vice versa), e.g. as 1st and 2nd arguments to a command, \RegisterOutputFiles.

  • The connection between the category and the extension is stored in two text labels with different names but same counter number, the basics are described in another question of mine(already solved, see details here: Writing text content as labels and refer to them with \nameref*)

  • \RegisterOutputFiles should create the file handles with some nice name, to remember them, say \HintsFile, \SolutionsFile etc. and open them for writing via \immediate\openout\HintsFile=\jobname.hints etc., all in a loop.

Now in the loop comes the failure, when the selfdefined command \NewWrite shows up the error message

 ! Missing \endcsname inserted. <to be read again> 
               \protect  l.84 ....prb,.hints,.sol,.explain,.idea,.concepts}

I am pretty sure, that I miss some expansion or similar, but I can not figure out, since \NewWrite works with direct injection of words like Hints etc.

Here is the (usual non-working) MWE

\documentclass{article}

\usepackage{ifthen}
\usepackage{etoolbox}
\usepackage{blindtext}
\usepackage{morewrites}
\usepackage{xcolor}
\usepackage{hyperref}%



\newrobustcmd{\NewWrite}[1]{%
\expandafter\newwrite\csname#1\endcsname%
}%


%% Later on... Create a handle from #1 and directly combine
%% it with filename #2 with \immediate\openout%
\newrobustcmd{\NewWriteOpenOut}[2]{%
\expandafter\newwrite\csname#1\endcsname%
\immediate\openout\csname #1\endcsname=#2%
}%


\makeatletter
\newcounter{pc@@filecounter}%      
\newcounter{pc@@totalfilecounter}%
\newcounter{pc@@loopcounter}%


%% Hopefully only text is in #1%
\newrobustcmd{\WriteOutputFileLabel}[1]{%
\refstepcounter{pc@@filecounter}%
\immediate\write\@auxout{%
\string\newlabel{pc::outputfile::\number\value{pc@@filecounter}}{{\thesection}{\thepage}{#1}{}}%
}% End of writing to AUX file
}%

\newrobustcmd{\WriteOutputFileNameLabel}[1]{%
\refstepcounter{pc@@filecounter}%
\immediate\write\@auxout{%
\string\newlabel{pc::outputfilename::\number\value{pc@@filecounter}}{{\thesection}{\thepage}{#1}{}}%
}% End of writing to AUX file
}%


\newrobustcmd{\RegisterOutputFiles}[2]{%
%%% Other code before
%
\setcounter{pc@@filecounter}{0}%
\forcsvlist{\WriteOutputFileLabel}{#1}%      Store the output file `categories` to label names
\setcounter{pc@@totalfilecounter}{\number\value{pc@@filecounter}}%
\setcounter{pc@@filecounter}{0}%
\forcsvlist{\WriteOutputFileNameLabel}{#2}%  Store the extensions to label 'names'
%
\noindent%
\textbf{\Large \textcolor{blue}{There are \number\value{pc@@totalfilecounter} file categories!}}
%
\setcounter{pc@@filecounter}{1}%
\setcounter{pc@@loopcounter}{\number\value{pc@@totalfilecounter}}
\addtocounter{pc@@loopcounter}{1}%
%
%
\begin{center}
\textbf{\textcolor{red}{Diagnostics}}
\end{center}
%
\whiledo{\number\value{pc@@filecounter} < \number\value{pc@@loopcounter}}{%
\noindent \nameref*{pc::outputfile::\number\value{pc@@filecounter}}  \(\longrightarrow \)  \jobname\nameref*{pc::outputfilename::\number\value{pc@@filecounter}}%
\stepcounter{pc@@filecounter}%
%% Creating the file handles file with error message `\protect` 
\NewWrite{\nameref*{pc::outputfile::\number\value{pc@@filecounter}}}%  %Creating file handle name `\anyname` 
\newline%
} %
%%% Other code after
}%

\makeatother


\begin{document}


\RegisterOutputFiles{Problem,Hints,Solution,Explanation,Ideas,Concepts}{.prb,.hints,.sol,.explain,.idea,.concepts}%

\blindtext

\end{document}

Notes:

You have to remove the % before \NewWrite in the loop and run pdflatex twice to see the final error message, the first one complains about missing references.

The code is not finished and not polished, it is pre-stage of some concept study for another package.

  • 1
    \nameref* isn't fully expandable, so it's illegal in \csname...\endcsname. – egreg May 30 '14 at 20:37
  • @egreg: How can I bypass it? I am not sticking to the label approach of something like key value hash (category -> file extension), I tried other approaches also via forcsvlist choosing elements from the lists, but failed also. – user31729 May 30 '14 at 20:38
  • 1
    Not using \nameref, of course. But I should study the macros for understanding how you could do it. Surely not with \label; after all, you open the file after you have created the handle. – egreg May 30 '14 at 20:40
  • @egreg: I can drop the \label way. What is wrong with opening the file after creating the handle? It would be nice feature to open it 'just' after creating the handle, but that is also not mandatory. – user31729 May 30 '14 at 20:46
  • That's what I said: you don't need the aux file, because you open the file after creating the handle. – egreg May 30 '14 at 21:01
2

You can't use \nameref* inside \csname...\endcsname, because it's not fully expandable. The association category-extension is best done with a different syntax, where the two parts are next to each other, so errors are less likely.

File hupfer-handle.tex

\documentclass{article}

\usepackage{etoolbox}
\usepackage{morewrites}
\usepackage{xcolor}

\makeatletter
%% create a list of the open streams
\listadd{\cs@stream@list}{}% initialize

%% a unique command for allocating a stream and opening it if the
%% optional name argument is given    
\newrobustcmd{\NewWrite}[2][]{%
  \listadd{\ch@stream@list}{#2}%
  \expandafter\newwrite\csname#2\endcsname
  \ifblank{#1}{}{\immediate\openout\@nameuse{#2}=#1 }%
}

%% a helper macro for counting the number of items in the csv argument
\newcommand{\ch@count@streams}[1]{\advance\@tempcnta\@ne}
%% a helper macro for separating the two parts at the slash
\newcommand\ch@open@stream[1]{\ch@open@stream@aux#1\@nil}
\def\ch@open@stream@aux#1/#2\@nil{%
  %% allocate a stream
  \NewWrite[\jobname.#2]{#1}%
  %% save the extension
  \@namedef{ch@stream@@#1}{.#2}%
}

\newrobustcmd{\RegisterOutputFiles}[2]{%
  %%% Other code before
  %
  \@tempcnta=\z@
  \forcsvlist{\ch@count@streams}{#1}%
  \forcsvlist{\ch@open@stream}{#1}%
  \noindent
  \textbf{\Large\textcolor{blue}{There are \number\@tempcnta\space file categories!}}%
  \begin{center}
  \textbf{\textcolor{red}{Diagnostics}}
  \end{center}
  \begingroup\parindent=\z@
  \renewcommand{\do}[1]{Handle ``##1'' with extension \texttt{\@nameuse{ch@stream@@##1}}\par}
  \dolistloop{\ch@stream@list}
  \endgroup
  %%% Other code after
}

\makeatother


\begin{document}

\RegisterOutputFiles{
  Problem/prb,
  Hints/hints,
  Solution/sol,
  Explanation/explain,
  Ideas/idea,
  Concepts/concepts
}

\end{document}

Output

enter image description here

File list

> ls hupfer-handle.*
hupfer-handle.aux       hupfer-handle.hints hupfer-handle.mw    hupfer-handle.sol
hupfer-handle.concepts  hupfer-handle.idea  hupfer-handle.pdf   hupfer-handle.tex
hupfer-handle.explain   hupfer-handle.log   hupfer-handle.prb
  • Well, I have to think about this approach and the details. – user31729 May 30 '14 at 21:39
  • I had time to think about and I agree with you, that keeping handle name/extension together is a good idea; I believe the former splitting of mine led to this 'silly' way of \nameref. I also used\forcsvlist and a former version of my code also had a stream list, but I failed, due to mentioned reason. This is a good solution! – user31729 Jun 1 '14 at 17:54
1

Here is another more straightforward solution:

\documentclass{article}

\newcount\tmpnum
\def\RegisterOutputFiles#1#2{\tmpnum=0 \def\listsummary{}%
   \registeroutputfilesA #1,,\relax #2,,\relax
}
\def\registeroutputfilesA #1,#2\relax #3,#4\relax{%
  \if^#1^The number of categories: \the\tmpnum{\tt\listsummary}\else
     \if^#3^\errmessage{?? less suffixes than categories.}\skiploop \fi
     \advance\tmpnum by1
     \csname newwrite\expandafter \endcsname \csname#1\endcsname
     \immediate \expandafter\openout \csname#1\endcsname =\jobname#3
     \edef\listsummary{\listsummary\endgraf
        The \jobname#3 is opened as \expandafter\string\csname#1\endcsname}%
     \registeroutputfilesA #2\relax #4\relax
  \fi\relax
}
\def\skiploop #1\fi\relax{\fi\fi}

\begin{document}

\RegisterOutputFiles {Problem,Hints,Solution,Explanation,Ideas,Concepts}
                     {.prb,.hints,.sol,.explain,.idea,.concepts}

\end{document}

I've added it here in order to show that usage of pure TeX can be more compact often.

  • I appreciate your effort doing this in PlainTeX, which shows me new path to usage of TeX code. – user31729 Jun 9 '14 at 22:57

Your Answer

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