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.

This question led to a new package:
bibleref-mouth

I am trying to write a command with an optional parameter, and feed the output to the \url command. My motivation is to generate links to online references using the bibleref package. That package already defined an API with optional parameters, so I don't want to change the syntax.

\url{\bibleverseurl{Amos}(3:7)}

But a command with optional arguments is apparently too fragile to be consumed by \url. The story is in my example code.

\documentclass{article}

\usepackage{makerobust}
\usepackage{hyperref}[2011/04/09 v6.82f]

% Macro that takes an argument and encloses it in angle brackets
\protect\def\parenmatch(#1){%
    MATCH$\langle#1\rangle$%
}

% Macro that does not take an argument
\protect\def\parenmismatch{%
    MISMATCH%
}

% Check whether there is a parenthesis in the input, then call
% \parenmatch or \parenmismatch
\makeatletter
\newcommand*{\matcher}{%
    \@ifnextchar{(}%
        \parenmatch%
        \parenmismatch%
}
\makeatother

% The \matcher command is probably fragile, so try to make it robust.
% Actually, I found that \matcher behaves identically to \robustmatcher
% in the examples below.
\DeclareRobustCommand{\robustmatcher}{\matcher}

\begin{document}

% This line correctly produces: MATCH<arg>
\robustmatcher(arg)

% This line correctly produces: MISMATCH!
\robustmatcher!

% This block emits an error: ! Use of \parenmatch doesn't match its definition.
% If I loosen the restrictions on the \parenmatch arguments, it
% only produces a different warning: ! Missing control sequence inserted.
% The evaluation does not respect protected commands, so it either fails to
% compile or it creates incorrect output.
% A better option is below.
\edef\problem{\robustmatcher()}
\problem

% This block correctly produces: MATCH<arg>
% As long as I use \protected@edef instead of \edef, it compiles successfully.
\makeatletter
\protected@edef\captured{\robustmatcher(arg)}
\makeatother
\captured

% Now I would like to use the command output as a link target.
% This was my goal all along.
% But the link doesn't have the address I would expect. It shows in the document as:
%   \protect\let\reserved@d=(\def\defMISMATCH{MISMATCH}\futurelet\@let@token\letMATCH$\delimiter"426830Aarg\delimiter"526930B$
% I tried sprinkling \protect and \expandafter commands in the definitions and
% applications, but nothing has helped.
\url{\captured}

\end{document}

Is there something about hyperref that is going to make this impossible? I expect that somehow I should be able to reorder the evaluations so that a plain string, already expanded, could be passed to \url safely.

share|improve this question
    
Have you tried using the \urldef command provided by the url package? Its advantage is that it creates url strings that are robust. –  Mico Nov 22 '11 at 4:42
    
I tried that and got the same result. The command \urldef\link\url{\captured} produced the same compiler error as \url{\captured} alone. That makes me believe that \urldef is meant to solve a different problem. –  Chad Parry Nov 22 '11 at 4:53
    
\protect\def should definitely be \protected\def (eTeX's way of having protection, unrelated to LateX's \protect). Also, \protected@edef\captured{\robustmatcher(arg)} doesn't capture anything: \robustmatcher is just not expanded. –  Bruno Le Floch Nov 22 '11 at 5:07
    
Your best bet is probably to put the \url command within the final macros: \def\urlmatcher{\@ifnextchar{(}\urlparenmatch\urlparenmismatch} \def\urlparenmatch(#1){\url{MATCH(#1)}} \def\urlparenmismatch{\url{MISMATCH!}}. –  Bruno Le Floch Nov 22 '11 at 5:12
    
Thanks for the suggestion, Bruno! I was hoping to not have to change the bibleref package internals, but I prefer an invasive solution to no solution at all. –  Chad Parry Nov 22 '11 at 5:16

1 Answer 1

up vote 3 down vote accepted

The problem you are facing is that it is not possible to parse optional arguments in an expandable way. Thus, \url will never see the expansion of your macros. At best (i.e., if the macros are made robust), the macros are not expanded at all, at worst, they are partly expanded, and blow up with typically some ! Undefined control sequence errors.

Your best bet is thus to put the \url command within the final macros rather than putting it around your macros. This way, the command which takes an optional argument is "outside" everything else, and can be as non-expandable as you wish.

\documentclass{article}
\usepackage{hyperref}[2011/04/09 v6.82f]
\def\urlmatcher{\@ifnextchar{(}\urlparenmatch\urlparenmismatch}
\def\urlparenmatch(#1){\url{MATCH(#1)}}
\def\urlparenmismatch{\url{MISMATCH!}}
\begin{document}
\urlmatcher Hello, world!
\urlmatcher(arg)   
\end{document}

To help you debug, you can use \show, very useful TeX primitive (used as \show\urlmatcher for instance, or in your example, \show\captured). I sometimes also use \tracingall.

share|improve this answer

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.