[Tex/LaTex] Removing a backslash from a character sequence

catcodesdetokenize

For indexing I wanted to write a macro \macroname that removes leading backslashes from macro names but leaves the names of environments untouched. That is

\macroname{\relax}   -->  relax
\macroname{itemize}  -->  itemize

where the --> is meant to be read as "expands to".

From my understanding, the following should work:

\newcommand\removebs[1]{\if#1\char92\else#1\fi}
\newcommand\macroname[1]{%
  \expandafter\removebs\detokenize{#1}}

However, its outcome is

\macroname{\relax}   -->  \relax   % literally
\macroname{itemize}  -->  itemize

By chance, actually, I found out that if I change the catcode of the backslash to "other" during the definition of the \removebs macro, it works:

{
  \catcode`\|=0
  |catcode`|\=12
  |global|def|removebs#1{|if#1\|else#1|fi}
}

\newcommand\macroname[1]{%
  \expandafter\removebs\detokenize{#1}}

Why is that? From TeX by Topic I'd expect that the comparison made by \if is what Philipp Lehman calls "category code agnostic".

Thanks for your answers!

Best Answer

Joseph has given a working solution. I'd like to explain what goes wrong with your code.

First attempt

\newcommand\removebs[1]{\if#1\char92\else#1\fi}
\newcommand\macroname[1]{%
  \expandafter\removebs\detokenize{#1}}

With \macroname{\relax} you get

\expandafter\removebs\detokenize{\relax}

and then (using to separate tokens and <space> do denote a space token)

\removebs • \ • r • e • l • a • x • <space>

which becomes

\if • \ • \char • 9 • 2 • \else • \ • \fi • r • e • l • a • x • <space>

and the comparison is between a category code 12 backslash and the token \char, which of course leads to false.

The code \char92 are instruction to print character 92 from the current font.

One might correct the code by checking against a real category code 12 backslash:

\makeatletter
\newcommand{\removebs}[1]{\if#1\@backslashchar\else#1\fi}
\makeatother

but the space produced by \detokenize after \relax would remain.

Second attempt

{
  \catcode`\|=0
  |catcode`|\=12
  |global|def|removebs#1{|if#1\|else#1|fi}
}

This works because it just implements the check against a category code 12 backslash, but it's not necessary, as the token is already available as \@backslashchar in the LaTeX kernel.

An alternative way, without global definitions and category changes, can be

\begingroup\lccode`\|=`\\
\lowercase{\endgroup\def\removebs#1{\if#1|\else#1\fi}}

where the only token that is converted to its lowercase equivalent is | (to a backslash).

Proposed code

% \makeatletter
% \newcommand{\removebs}[1]{\if#1\@backslashchar\else#1\fi}
% \makeatother
\begingroup\lccode`\|=`\\
\lowercase{\endgroup\def\removebs#1{\if#1|\else#1\fi}}
\newcommand{\macroname}[1]{\expandafter\removebs\string#1}

Since all you want is to get the argument letters, it doesn't matter if the first letter in \macroname{itemize} has category code 12.