3

I'm using subfiles package to handle a large project.

I place the main file in the main directory, and subfile in a subdirectory. In order to use the \input command in the subfile while compiling in the main file, I need to manually change the input path before the \subfile command and restore afterwards.

Sample code goes like this (copied from this answer),

\makeatletter
\providecommand*{\input@path}{}
\edef\input@path{{subfile_path/}\input@path}% subfolder path
\makeatother

\subfile{subfile_path/subfile}

\makeatletter
\providecommand*{\input@path}{}
\edef\input@path{{./}\input@path}% restore
\makeatother

I want to ask is it possible to encapsulate the "makeatletter" codes into a command. I tried to define a new command

\newcommand{\autosubfile}[2]{
  \makeatletter
  \providecommand*{\input@path}{}
  \edef\input@path{{#1/}\input@path}% subfolder path
  \makeatother
  \subfile{#1/#2}
  \makeatletter
  \providecommand*{\input@path}{}
  \edef\input@path{{./}\input@path}% restore
  \makeatother
}

but if failed. Here is the error message:

ERROR: LaTeX Error: Command \input already defined.

--- TeX said ---
               Or name \end... illegal, see p.192 of the manual.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return>  for immediate help.
 ...                                              

The etoolbox package mentioned in this answer may be helpful, but I do not know how to change the number of arguments of \subfile, or define a new command to execute the "makeatletter" snippet.

Thanks for helping me.

anecdote
  • 461
  • Just move \makeatletter and \makeatother before and after the definition of \autosubfile. But you're not restoring the value, that way. – egreg Mar 15 '18 at 18:32
  • The doubled \providecommand inside is weird –  Mar 15 '18 at 18:33
  • @egrep, you are right, I'm prepending the path each time invoking it. What I intended to do was \edef\input@path{{./}}, but your \saved@input@path is much better. It restores the path with whatever it was before! Thanks. – anecdote Mar 15 '18 at 19:03
  • @ChristianHupfer, the 2nd one can be removed, although it does nothing. – anecdote Mar 15 '18 at 19:04
  • @anecdote: Yes, but \providecommand should be once (and at best not inside of another macro) –  Mar 15 '18 at 19:06

2 Answers2

5

You should have \makeatletter before any definition that uses commands with @ in their name.

You're also equivocating the answer you're referring to.

  1. \providecommand{\input@path}{} should be done only once.
  2. You're continuously prepending things to \input@path rather than restoring it.

Here's how you can do:

\makeatletter
\providecommand*{\input@path}{} % in case it's undefined
\newcommand{\autosubfile}[2]{% <- don't forget
  % save the current \input@path
  \let\saved@input@path\input@path
  \edef\input@path{{#1/}\input@path}% subfolder path
  \subfile{#1/#2}%
  % restore \input@path to its previous value
  \let\input@path\saved@input@path
}
\makeatother
egreg
  • 1,121,712
2

Your mistake (quite common when users are getting to know with TeX features) is based to misunderstanding the cooperation of tokenizer and expand procesor in TeX. Tokenizer creates control sequences like \foo as one token always at the time when the data are read first from input file. The macros are saved into TeX memory as sequences of tokens created by tokenizer, no as strings. And expand processor changes sequences of tokens to another sequences of tokens when a macros are expanded.

Illustratively:

\def\yourmacro{
    \makeatletter %... instruction for tokenizer in your example
        % this is not processed when macro body is read, 
        % this is saved only to TeX memory
    \foo@bar  %... tokenizer creates \foo and @ and b and a and r
              % and this is saved to TeX memory in macro body
    \makeaother %... instruction for tokenizer without effect
}

... usage of \yourmacro % expand processor replaces it by \makeatletter
                        % followed by \foo, @, b, a, r and \makeatother.
                        % Now, \makeatletter is instruction for tokenizer without
                        % effect because \foo, @, b, a, r are tokenized already.

On the other hand correct usage:

\makeatletter %... instrution for tokenizer, all new read data are
              % tokenized differently now.
\def\yourmacro{
   ...
   \foo@bar   % one token \foo@bar is saved to TeX memory in macro body
   ...
}
\makeatother % instruction for tokenizer to behave normally

... usage of \yourmacro % expand processor replaces it by ... \foo@bar ...
wipet
  • 74,238