2

Compiling

\documentclass{article}
\usepackage{etoolbox,xstring}

\newcommand{\strendswithfoo}{\IfEndWith{bar-foo}{foo}{true}{false}}

\newtoggle{strendswithfoo}
\settoggle{strendswithfoo}{\strendswithfoo}

\begin{document}

command \textbackslash strendswithfoo = \strendswithfoo

toggle strendswithfoo = \iftoggle{strendswithfoo}{true}{false}

\end{document}

leads to the following errors:

./test.tex:7: Missing \endcsname inserted.
<to be read again> 
                   \let 
l.7 \settoggle{strendswithfoo}{\strendswithfoo}

The control sequence marked <to be read again> should
not appear between \csname and \endcsname.


./test.tex:7: Argument of \reserved@a has an extra }.
<inserted text> 
                \par 
l.7 \settoggle{strendswithfoo}{\strendswithfoo}

I've run across a `}' that doesn't seem to match anything.
For example, `\def\a#1{...}' and `\a}' would produce
this error. If you simply proceed now, the `\par' that
I've just inserted will cause me to report a runaway
argument that might be the root of the problem. But if
your `}' was spurious, just type `2' and it will go away.

Runaway argument?
{\@firstoftwo {\let \@xs@assign \@xs@expand@and@detokenize \@xs@IfEndWith@@ \ETC.
./test.tex:7: Paragraph ended before \reserved@a was complete.
<to be read again> 
                   \par 
l.7 \settoggle{strendswithfoo}{\strendswithfoo}

I suspect you've forgotten a `}', causing me to apply this
control sequence to too much text. How can we recover?
My plan is to forget the whole thing and hope for the best.

./test.tex:7: Package etoolbox Error: Invalid boolean value '\let \reserved@d =*\def \par .
<recently read>  

l.7 \settoggle{strendswithfoo}{\strendswithfoo}

Valid boolean values are 'true' and 'false'.

The output document contains:

command \strendswithfoo = true
toggle strendswithfoo = false

I would like the toggle strendswithfoo to be true. Happy to switch away from xstring if there is a better solution.

  • As with almost any xstring macro: They are not expandable. Switching to a expl3 based solution is better, in my point of view – user31729 Oct 18 '17 at 16:30
2

A different approach using listofitems, whose results are expandable. That is to say, the actual \readlist is not expandable, but the array of data it produces is expandable.

In this implementation, I search the test string for all occurrences of "foo", using it as a list separator. The text that isn't "foo" is stored in the \foosep array, \foosep[1], \foosep[2], etc. where the index 1 is the text to the left of the leftmost "foo", the index 2 is the text between the 1st and 2nd "foo", etc.

Negative indices such as [-1] merely starts the counting leftward, from the rightmost "foo". If the string ended in "foo", then the expansion of \foosep[-1], representing the text to the right of the rightmost "foo", will be empty, and so I test its emptiness to determine whether to set the toggle to true or false.

\documentclass{article}
\usepackage{etoolbox,listofitems}

\newtoggle{strendswithfoo}
\def\toggletmp{\settoggle{strendswithfoo}}

\newcommand{\teststrendswithfoo}[1]{%
  \setsepchar{foo}%
  \readlist\foosep{#1}%
  \edef\strendswithfoo{\if\relax\foosep[-1]\relax{true}\else{false}\fi}%
  \expandafter\toggletmp\strendswithfoo
}

\begin{document}
\teststrendswithfoo{bar-foo}
toggle strendswithfoo = \iftoggle{strendswithfoo}{true}{false}

\teststrendswithfoo{foo-bar}
toggle strendswithfoo = \iftoggle{strendswithfoo}{true}{false}
\end{document}

enter image description here

0

One solution is to make the arguments to \IfEndWith \toggletrue and \togglefalse. I find this rather unsatisfying and a little more like spaghetti code than I'd like but it is short.

\documentclass{article}
\usepackage{etoolbox,xstring}

\newtoggle{strendswithfoo}

\IfEndWith{bar-foo}{foo}{\toggletrue}{\togglefalse}{strendswithfoo}

\begin{document}

toggle strendswithfoo = \iftoggle{strendswithfoo}{true}{false}

\end{document}

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.