Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

The \apptocmd command from etoolbox is giving me an error I don't understand. I know how to work around the error in this particular case, but I'd like to understand what it's complaining about so that I can fix similar errors in the future.

This is the error I'm getting:

[debug] tracing \apptocmd on input line 28
[debug] analyzing \H@old@part
[debug] ++ control sequence is defined
[debug] ++ control sequence is a macro
[debug] ++ control sequence is a macro with parameters
[debug] -- nested patching command and parameters in patch
[debug] -> the patching command seems to be nested in the
[debug]    argument to some other command
[debug] -> the patch text seems to contain # characters
[debug] -> either avoid nesting or use # characters with
[debug]    category code 12 in the patch text
[debug] -> simply doubling the # characters will not work

! Package cont Error: can't patch \H@old@part.

See the cont package documentation for explanation.
Type  H <return>  for immediate help.
 ...                                              

l.28     }

? 

The following produces the above error:

\documentclass{report}
\usepackage{etoolbox}
\tracingpatches
\usepackage{hyperref}
\makeatletter

% error handler for patching commands
\newrobustcmd{\cont@err}[1]{\PackageError{cont}{can't patch \protect#1}{}}%

% contains the name of the current part
\gdef\cont@name@part{\PackageError{cont}{not in a part}{}}

% patch \part to save the part name to \cont@name@part.  note
% that \part is a parameterless macro that has \@part as its last
% token, which does take the arguments.  Thus, \part can't be
% appended to; \@part has to be appended to instead.
%
% The hyperref package redefines \@part, so when hyperref is
% loaded \H@old@part needs to be patched instead.
\ifdef{\H@old@part}{
  \apptocmd{\H@old@part}{%
    \gdef\cont@name@part{#2}%
  }{}{\cont@err{\H@old@part}}
}{
  \apptocmd{\@part}{%
    \gdef\cont@name@part{#2}%
  }{}{\cont@err{\H@old@part}}
}

\begin{document}
\part{first part}
this part's name is ``\cont@name@part''
\end{document}

The following also doesn't work:

\catcode`\#=12
\ifdef{\H@old@part}{
  \apptocmd{\H@old@part}{%
    \gdef\cont@name@part{#2}%
  }{}{\cont@err{\H@old@part}}
}{
  \apptocmd{\@part}{%
    \gdef\cont@name@part{#2}%
  }{}{\cont@err{\H@old@part}}
}
\catcode`\#=6

It produces the following error:

[debug] analyzing \H@old@part
[debug] ++ control sequence is defined
[debug] ++ control sequence is a macro
[debug] ++ control sequence is a macro with parameters
[debug] -- macro cannot be retokenized cleanly
[debug] -> the macro may have been defined under a category
[debug]    code regime different from the current one
[debug] -> the replacement text may contain special control
[debug]    sequence tokens formed with \csname...\endcsname;
[debug] -> the replacement text may contain carriage return,
[debug]    newline, or similar characters

I can work around the problem by doing the following instead:

\expandafter\apptocmd\expandafter{%
  \csname\ifdef{\H@old@part}{H@old}{}@part\endcsname%
}{%
  \gdef\cont@name@part{#2}%
}{}{\cont@error{\@part}}

However, I'd like to know how to fix the error while using the former approach (\apptocmd in an argument of \ifdef).

share|improve this question
    
Does the solution in the text that you quote not work? debug] -> either avoid nesting or use # characters with [debug] category code 12 in the patch text ie put \catcode\#=12` before the ifdef and \catcode\#=6` after it –  David Carlisle Apr 14 '12 at 20:49
    
Are you looking for a working solution or an explanation why \apptocmd is giving the error message? \ifdef is a command, so the error is correct. \ifcsname H@old@part\endcsname \apptocmd{\H@old@part}{% \gdef\cont@name@part{#2}% }{}{\cont@err{\H@old@part}} \else \apptocmd{\@part}{% \gdef\cont@name@part{#2}% }{}{\cont@err{\H@old@part}} \fi works. –  Stephan Lehmke Apr 14 '12 at 20:53
    
@DavidCarlisle: see updated question –  Richard Hansen Apr 14 '12 at 20:55
    
@StephanLehmke: I'm looking for an explanation that I can use to troubleshoot and fix similar errors in the future (that might have nothing to do with \ifdef). A working solution often helps explain why it is an error, but not always. –  Richard Hansen Apr 14 '12 at 20:59
    
the patch command clearly uses catcode changes so it's like \verb while there are things you can do to make \verb half work in some arguments, the basic rule of TeX is that commands using catcode changes do not work in the arguments of other commands. –  David Carlisle Apr 14 '12 at 21:26
add comment

3 Answers 3

up vote 8 down vote accepted

The patch command clearly uses catcode changes so it's like \verb while there are things you can do to make \verb half work in some arguments, the basic rule of TeX is that commands using catcode changes do not work in the arguments of other commands.

The reason for this is that catcode changes change the way that characters are converted to tokens by TeX's scanner, but macro arguments are scanned and tokenized while scanning the argument looking for the closing brace so what is passed to the macro as #1 or #2 etc. is not a list of characters it is a list of tokens and catcode changes have no effect on the interpretation of tokens.

share|improve this answer
add comment

The problem is in the #2 in the argument. Use

\ifdefined\H@old@part
  \apptocmd{\H@old@part}{\gdef\cont@name@part{#2}}{}{\cont@err{\H@old@part}}
\else
  \apptocmd{\@part}{\gdef\cont@name@part{#2}}{}{\cont@err{\@part}}
\fi

I'm developing an extended version of xpatch and I'm glad to tell you that

\documentclass{report}
\usepackage{etoolbox} % for \ifdef
\usepackage{regexpatch} % the experimental version of xpatch

\usepackage{hyperref} 

\makeatletter
\ifdef{\H@old@part}
  {%
   \regexpatchcmd{\H@old@part}{$}{\c{gdef}\c{cont@name@part}\cB\{\cP\#2\cE\}}{}{}%
  }
  {%
   \regexpatchcmd{\@part}{$}{\c{gdef}\c{cont@name@part}\cB\{\cP\#2\cE\}}{}{}%
  }
\makeatother

\begin{document}


\part{first part}
this part's name is ``\makeatletter\cont@name@part\makeatother''
\end{document}

seems to do what's expected, with or without hyperref. The package has been uploaded to CTAN.

share|improve this answer
    
but why is #2 a problem? –  Richard Hansen Apr 14 '12 at 21:02
    
@RichardHansen There are always problems with # in arguments and in replacement texts; etoolbox rejects dangerous patches involving them in some cases. I didn't try doubling it, but the straight method seems the best road: \ifdef does just that. –  egreg Apr 14 '12 at 21:07
    
How would I deal with this error in non-ifdef cases, for example \afterpage{\apptocmd{...}{...}{}{}}? –  Richard Hansen Apr 14 '12 at 21:19
    
@RichardHansen This won't definitely work, sorry. It might with the regex module of LaTeX3, but it's experimental. I may look into it. –  egreg Apr 14 '12 at 21:26
add comment

One possibility to solve this problem would be to pre-define the patching commands and use them in a 'wrapped' form in contexts where plain # are not allowed:

\makeatletter
\newcommand\defappcommand
{%
  \begingroup
    \catcode`\#=12
    \@defappcommand
}
\newcommand\@defappcommand[2]
{%
  \endgroup
  \def#1{#2}%
}
\makeatother

and then

\defappcommand\apptoH@old@part
{\apptocmd{\H@old@part}{\gdef\cont@name@part{#2}}{}{\cont@err{\H@old@part}}}

\defappcommand\appto@part
{\apptocmd{\@part}{\gdef\cont@name@part{#2}}{}{\cont@err{\@part}}}

\ifdef{\H@old@part}{
  \apptoH@old@part
}{
  \appto@part
}
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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