Sorry for being late to the party. :-)
expl3/xparse/ltcmd bring along nice tools for case-forking. Since the 2020-10-01 release expl3 and xparse (the latter re-branded as ltcmd and deprived of things that are not considered good practice any more) are included in the LaTeX 2e-format. So nowadays you could use expl3/ltcmd "out of the box" without loading additional packages. But in 2012, when the question was asked, xparse/expl3 needed to be loaded as additional packages while the question requests things to be done without loading additional packages. Thus I assume that the focus of the question is on "old school code".
You can easily avoid any \if..\else..\fi-thingie without loading whatsoever additional packages if you do things in terms of delimited arguments:
\makeatletter \errorcontextlines=10000 %%============================================================================= %% PARAPHERNALIA: %% \UD@firstoftwo, \UD@secondoftwo, \UD@stopromannumeral, \UD@CheckWhetherNull, %%============================================================================= \newcommand\UD@firstoftwo[2]{#1}% \newcommand\UD@secondoftwo[2]{#2}% \@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}% %%----------------------------------------------------------------------------- %% Check whether argument is empty: %%............................................................................. %% \UD@CheckWhetherNull{<Argument which is to be checked>}% %% {<Tokens to be delivered in case that argument %% which is to be checked is empty>}% %% {<Tokens to be delivered in case that argument %% which is to be checked is not empty>}% %% %% The gist of this macro comes from Robert R. Schneck's \ifempty-macro: %% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J> \newcommand\UD@CheckWhetherNull[1]{% \romannumeral\expandafter\UD@secondoftwo\string{\expandafter \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{% \expandafter\UD@stopromannumeral\UD@firstoftwo}% }% %%============================================================================= \@ifdefinable\UD@GobbleToExclam{\long\def\UD@GobbleToExclam#1!{}}% \@ifdefinable\UD@abcFork{% \long\def\UD@abcFork#1!a!b!c!#2#3!!!!{\UD@stopromannumeral#2}% }% \newcommand\dothis[1]{% \romannumeral\expandafter\UD@CheckWhetherNull \expandafter{\UD@GobbleToExclam#1!}{% \UD@abcFork !#1!b!c!{so you typed a}% <-this is \UD@abcFork's 2nd arg if #1 = a !a!#1!c!{now this is b}% <-this is \UD@abcFork's 2nd arg if #1 = b !a!b!#1!{you want me to do c?}% <-this is \UD@abcFork's 2nd arg if #1 = c !a!b!c!{[nada]}% <-this is \UD@abcFork's 2nd arg if #1 neither a nor b nor c !!!!% <-this is the delimiter of \UD@abcFork's 3rd argument }{\UD@stopromannumeral[nada]}% <-This is done if #1 contains ! and thus could erroneously match the delimiter !a!b!c! }% \makeatother \documentclass{article} \begin{document} \dothis{a} \dothis{b} \dothis{c} \dothis{e} \dothis{!e!} \end{document}

If you wish to combine cases, e.g. "You typed a or d", you can combine the above forking-technique with a routine for extracting the K-th argument from a list of undelimited arguments so that the number K is provided by the forking-mechanism:
\makeatletter \errorcontextlines=10000 %%============================================================================= %% PARAPHERNALIA: %% \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond, %% \UD@stopromannumeral, \UD@CheckWhetherNull, %%============================================================================= \newcommand\UD@firstoftwo[2]{#1}% \newcommand\UD@secondoftwo[2]{#2}% \newcommand\UD@PassFirstToSecond[2]{#2{#1}}% \@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}% %%----------------------------------------------------------------------------- %% Check whether argument is empty: %%............................................................................. %% \UD@CheckWhetherNull{<Argument which is to be checked>}% %% {<Tokens to be delivered in case that argument %% which is to be checked is empty>}% %% {<Tokens to be delivered in case that argument %% which is to be checked is not empty>}% %% %% The gist of this macro comes from Robert R. Schneck's \ifempty-macro: %% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J> \newcommand\UD@CheckWhetherNull[1]{% \romannumeral\expandafter\UD@secondoftwo\string{\expandafter \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{% \expandafter\UD@stopromannumeral\UD@firstoftwo}% }% %%============================================================================= %% %% Extract K-th inner undelimited argument: %% %% \ExtractKthArg{<integer K>}% %% {<tokens in case list of undelimited args doesn't have a k-th argumnent>}% %% {<list of undelimited args>} % %% %% In case there is no K-th argument in <list of indelimited args> : %% Does deliver <tokens in case list of undelimited args doesn't have a k-th argumnent. %% In case there is a K-th argument in <list of indelimited args> : %% Does deliver that K-th argument with one level of braces removed. %% %% Examples: %% %% \ExtractKthArg{0}{not available}{ABCDE} yields: not available %% %% \ExtractKthArg{3}{not available}{ABCDE} yields: C %% %% \ExtractKthArg{3}{not available}{AB{CD}E} yields: CD %% %% \ExtractKthArg{4}{not available}{{001}{002}{003}{004}{005}} yields: 004 %% %% \ExtractKthArg{6}{not available}{{001}{002}{003}} yields: not available %% %% Due to \romannumeral-expansion the result can be obtained by triggering %% two expansion-steps. %%============================================================================= \newcommand\ExtractKthArg[2]{% \romannumeral% % #1: <integer number K> % #2: <action if there is no K-th argument> \expandafter\UD@ExtractKthArgCheck \expandafter{\romannumeral\number\number#1 000}{#2}% }% \newcommand\UD@ExtractKthArgCheck[3]{% \UD@CheckWhetherNull{#1}{\UD@stopromannumeral#2}{% empty \expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}% }% }% \begingroup \def\UD@ExtractFirstArgLoop#1{% \endgroup \@ifdefinable\UD@RemoveTillFrozenrelax{% \long\def\UD@RemoveTillFrozenrelax##1##2#1{{##1}}% }% \newcommand\UD@ExtractKthArgLoop[3]{% \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo##3{}.}{\UD@stopromannumeral##2}{% \UD@CheckWhetherNull{##1}{% \UD@ExtractFirstArgLoop{##3#1}% }{% \expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}##3}% {\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}##1}{##2}}% }% }% }% }% \expandafter\expandafter\expandafter\UD@ExtractFirstArgLoop \expandafter\expandafter\expandafter{% \expandafter\expandafter\ifnum0=0\fi}% %% Usage of frozen-\relax as delimiter is for speeding things up by reducing the %% amount of iterations needed. I chose frozen-\relax because David Carlisle %% pointed out in <https://tex.stackexchange.com/a/578877> %% that frozen-\relax cannot be (re)defined in terms of \outer and cannot be %% affected by \uppercase/\lowercase. %% %% \UD@ExtractFirstArg's argument may contain frozen-\relax: %% The only effect is that internally more iterations are needed for %% obtaining the result. \newcommand\UD@ExtractFirstArgLoop[1]{% \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}% {\expandafter\UD@stopromannumeral\UD@firstoftwo#1{}}% {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillFrozenrelax#1}}% }% %% End of code for \ExtractKthArg. %%============================================================================= \@ifdefinable\UD@GobbleToExclam{\long\def\UD@GobbleToExclam#1!{}}% \@ifdefinable\UD@abcdeFork{% \long\def\UD@abcdeFork#1!a!b!c!d!e!#2#3!!!!{#2}% }% \newcommand\dothis[1]{% \romannumeral\expandafter\UD@secondoftwo \ExtractKthArg{% %==\ExtractKthArg's 1st argument=<integer K>=============================== \expandafter\UD@CheckWhetherNull\expandafter{\UD@GobbleToExclam#1!}{% \UD@abcdeFork !#1!b!c!d!e!{1}% <- #1 = a: deliver 1st argument !a!#1!c!d!e!{2}% <- #1 = b: deliver 2nd argument !a!b!#1!d!e!{3}% <- #1 = c: deliver 3rd argument !a!b!c!#1!e!{1}% <- #1 = d: deliver 1st argument !a!b!c!d!#1!{4}% <- #1 = e: deliver 4th argument !a!b!c!d!e!{5}% <- #1 doesn't contain ! and is neither a nor b nor c nor d nor e: deliver 5th argument !!!!% }{5}% <- #1 does contain ! and thus is neither a nor b nor c nor d nor e: deliver 5th argument %==End of \ExtractKthArg's 1st argument==================================== }{%==\ExtractKthArg's 2nd argument=<tokens in case list of undelimited args % doesn't have a k-th argumnent> It is left empty as a K-th argument is % in the list for each value K that can occur. }{%==\ExtractKthArg's 3rd argument=<list of undelimited arguments>=========== {You typed a or you typed d.}% - 1st argument {You typed b.}% - 2nd argument {You typed c.}% - 3rd argument {You typed e.}% - 4th argument {You typed something which is neither a nor b nor c nor d nor e.}% - 5th argument }% }% %%============================================================================= \makeatother \documentclass{article} \begin{document} \dothis{a} \dothis{b} \dothis{c} \dothis{d} \dothis{e} \dothis{f} \dothis{!f!} \end{document}

If you need to fork many different cases so that a single forking-macro would require a delimiter which is cluttered, you can use more than one forking macro.
E.g., instead of \UD@abcdeFork you can combine usage of \UD@abcFork and \UD@deFork:
\makeatletter \errorcontextlines=10000 %%============================================================================= %% PARAPHERNALIA: %% \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond, %% \UD@stopromannumeral, \UD@CheckWhetherNull, %%============================================================================= \newcommand\UD@firstoftwo[2]{#1}% \newcommand\UD@secondoftwo[2]{#2}% \newcommand\UD@PassFirstToSecond[2]{#2{#1}}% \@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}% %%----------------------------------------------------------------------------- %% Check whether argument is empty: %%............................................................................. %% \UD@CheckWhetherNull{<Argument which is to be checked>}% %% {<Tokens to be delivered in case that argument %% which is to be checked is empty>}% %% {<Tokens to be delivered in case that argument %% which is to be checked is not empty>}% %% %% The gist of this macro comes from Robert R. Schneck's \ifempty-macro: %% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J> \newcommand\UD@CheckWhetherNull[1]{% \romannumeral\expandafter\UD@secondoftwo\string{\expandafter \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{% \expandafter\UD@stopromannumeral\UD@firstoftwo}% }% %%============================================================================= %% %% Extract K-th inner undelimited argument: %% %% \ExtractKthArg{<integer K>}% %% {<tokens in case list of undelimited args doesn't have a k-th argumnent>}% %% {<list of undelimited args>} % %% %% In case there is no K-th argument in <list of indelimited args> : %% Does deliver <tokens in case list of undelimited args doesn't have a k-th argumnent. %% In case there is a K-th argument in <list of indelimited args> : %% Does deliver that K-th argument with one level of braces removed. %% %% Examples: %% %% \ExtractKthArg{0}{not available}{ABCDE} yields: not available %% %% \ExtractKthArg{3}{not available}{ABCDE} yields: C %% %% \ExtractKthArg{3}{not available}{AB{CD}E} yields: CD %% %% \ExtractKthArg{4}{not available}{{001}{002}{003}{004}{005}} yields: 004 %% %% \ExtractKthArg{6}{not available}{{001}{002}{003}} yields: not available %% %% Due to \romannumeral-expansion the result can be obtained by triggering %% two expansion-steps. %%============================================================================= \newcommand\ExtractKthArg[2]{% \romannumeral% % #1: <integer number K> % #2: <action if there is no K-th argument> \expandafter\UD@ExtractKthArgCheck \expandafter{\romannumeral\number\number#1 000}{#2}% }% \newcommand\UD@ExtractKthArgCheck[3]{% \UD@CheckWhetherNull{#1}{\UD@stopromannumeral#2}{% empty \expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}% }% }% \begingroup \def\UD@ExtractFirstArgLoop#1{% \endgroup \@ifdefinable\UD@RemoveTillFrozenrelax{% \long\def\UD@RemoveTillFrozenrelax##1##2#1{{##1}}% }% \newcommand\UD@ExtractKthArgLoop[3]{% \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo##3{}.}{\UD@stopromannumeral##2}{% \UD@CheckWhetherNull{##1}{% \UD@ExtractFirstArgLoop{##3#1}% }{% \expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}##3}% {\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}##1}{##2}}% }% }% }% }% \expandafter\expandafter\expandafter\UD@ExtractFirstArgLoop \expandafter\expandafter\expandafter{% \expandafter\expandafter\ifnum0=0\fi}% %% Usage of frozen-\relax as delimiter is for speeding things up by reducing the %% amount of iterations needed. I chose frozen-\relax because David Carlisle %% pointed out in <https://tex.stackexchange.com/a/578877> %% that frozen-\relax cannot be (re)defined in terms of \outer and cannot be %% affected by \uppercase/\lowercase. %% %% \UD@ExtractFirstArg's argument may contain frozen-\relax: %% The only effect is that internally more iterations are needed for %% obtaining the result. \newcommand\UD@ExtractFirstArgLoop[1]{% \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}% {\expandafter\UD@stopromannumeral\UD@firstoftwo#1{}}% {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillFrozenrelax#1}}% }% %% End of code for \ExtractKthArg. %%============================================================================= \@ifdefinable\UD@GobbleToExclam{\long\def\UD@GobbleToExclam#1!{}}% \@ifdefinable\UD@abcFork{% \long\def\UD@abcFork#1!a!b!c!#2#3!!!!{#2}% }% \@ifdefinable\UD@deFork{% \long\def\UD@deFork#1!d!e!#2#3!!!!{#2}% }% \newcommand\dothis[1]{% \romannumeral\expandafter\UD@secondoftwo \ExtractKthArg{% %==\ExtractKthArg's 1st argument=<integer K>=============================== \expandafter\UD@CheckWhetherNull\expandafter{\UD@GobbleToExclam#1!}{% \UD@abcFork !#1!b!c!{1}% <- #1 = a: deliver 1st argument !a!#1!c!{2}% <- #1 = b: deliver 2nd argument !a!b!#1!{3}% <- #1 = c: deliver 3rd argument !a!b!c!{% <- #1 does not contain ! and is neither a nor b nor c / in this case number is delivered by \UD@deFork \UD@deFork !#1!e!{1}% <- #1 = d: deliver 1st argument !d!#1!{4}% <- #1 = e: deliver 4th argument !d!e!{5}% <- #1 (doesn't contain ! and) is neither (a nor b nor c nor) d nor e: deliver 5th argument !!!!% }% !!!!% }{5}% <- #1 does contain ! and thus is neither a nor b nor c nor d nor e: deliver 5th argument %==End of \ExtractKthArg's 1st argument==================================== }{%==\ExtractKthArg's 2nd argument=<tokens in case list of undelimited args % doesn't have a k-th argumnent> It is left empty as a K-th argument is % in the list for each value K that can occur. }{%==\ExtractKthArg's 3rd argument=<list of undelimited arguments>=========== {You typed a or you typed d.}% - 1st argument {You typed b.}% - 2nd argument {You typed c.}% - 3rd argument {You typed e.}% - 4th argument {You typed something which is neither a nor b nor c nor d nor e.}% - 5th argument }% }% %%============================================================================= \makeatother \documentclass{article} \begin{document} \dothis{a} \dothis{b} \dothis{c} \dothis{d} \dothis{e} \dothis{f} \dothis{!f!} \end{document}

\pdfstrcmp?\str_case:nnFconstruct that expl3 makes available through e.g.\usepackage{xparse}. I was initially put off by the unusual characters in the macro name, but that was a mistake — it just has to be surrounded by\ExplSyntaxOn,\ExplSyntaxOffto make that syntax valid.\str_case:nnFis documented in interface3.pdf.