summaryrefslogtreecommitdiff
path: root/tex/context/base/mkii/syst-gen.mkii
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/mkii/syst-gen.mkii')
-rw-r--r--tex/context/base/mkii/syst-gen.mkii4459
1 files changed, 4459 insertions, 0 deletions
diff --git a/tex/context/base/mkii/syst-gen.mkii b/tex/context/base/mkii/syst-gen.mkii
new file mode 100644
index 000000000..8b71b77d5
--- /dev/null
+++ b/tex/context/base/mkii/syst-gen.mkii
@@ -0,0 +1,4459 @@
+%D \module
+%D [ file=syst-gen,
+%D version=1996.03.20,
+%D title=\CONTEXT\ System Macros,
+%D subtitle=General,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% nagaan : \ifinstringelse in syst-ext.tex
+% do => p! dodo pp! dododo ppp!
+% todo: \donetrue etc local maken
+
+%D The following macros are responsible for the interaction
+%D with \CONTEXT. These macros have proven their use. These
+%D macros are optimized as far as possible within of course,
+%D the know how of the author.
+%D
+%D In this module we also show some of the optimizations,
+%D mainly because we don't want to forget them and start doing
+%D things over and over again. If showing them has a learing
+%D effect for others too, we've surved another purpose too.
+
+%D \macros
+%D {abortinputifdefined}
+%D
+%D Because this module can be used in a different context, we
+%D want to prevent it being loaded more than once. This can be
+%D done using:
+%D
+%D \starttyping
+%D \abortinputifdefined\command
+%D \stoptyping
+%D
+%D where \type{\command} is a command defined in the module
+%D to be loaded only once.
+%D
+%D \starttyping
+%D \def\abortinputifdefined#1%
+%D {\ifx#1\undefined
+%D \let\next=\relax
+%D \else
+%D \let\next=\endinput
+%D \fi
+%D \next}
+%D \stoptyping
+%D
+%D This macro can be speed up in terms of speed as well as
+%D memory. Because this is a nice example of a bit strange
+%D command (\type{\endinput}), we spend some more lines on this.
+%D
+%D If we perform such actions directly, we can say:
+%D
+%D \starttyping
+%D \ifx\somecommand\undefined
+%D \let\next=\relax
+%D \else
+%D \let\next=\endinput
+%D \fi
+%D \next
+%D \stoptyping
+%D
+%D We need the \type{\next} because we need to end the
+%D \type{\fi}. The efficient one is:
+%D
+%D \starttyping
+%D \ifx\somecommand\undefined
+%D \else
+%D \expandafter\endinput
+%D \fi
+%D \stoptyping
+%D
+%D Because \type{\endinput} comes into action after the current
+%D line, we can also say:
+%D
+%D \starttyping
+%D \ifx\somecommand\undefined \else \endinput \fi
+%D \stoptyping
+%D
+%D When we define a macro, we tend to use a format which
+%D shows as besat as can how things are done. \TEX\ however
+%D stores the definitions as a sequence of tokens, so in fact
+%D we can use a formatted definition:
+
+\def\abortinputifdefined#1%
+ {\ifx#1\undefined \else
+ \endinput
+ \fi}
+
+%D which also works. Keep in mind that this is entirely due to
+%D the fact that \type{\endinput} after the line, i.e. at the
+%D end of the macro. We therefore can burry this primitive quite
+%D deep in code.
+
+%D And because this module implements \type{\writestatus}, we
+%D just say:
+
+% \abortinputifdefined\writestatus
+
+%D \macros
+%D {overloaded, superseded, forwarded, predefined}
+%D
+%D This prefix is used as signal for the \CONTEXT\ dependency
+%D checking features. The first four prefixes don't do anything
+%D useful, apart from signaling parsers.
+
+\def\overloaded{} % local change at the macro level
+\def\superseded{} % global change at the module level, replaces previous definitions
+\def\predefined{} % defined (first) here, but may be redefined anytime (no need for \overloaded)
+\def\forwarded {} % definition at the module level, only done when undefined
+
+\def\forwarded#1#2{\ifx#2\undefined\else\expandafter\gobbleforwarded\fi#1#2}
+
+\def\gobbleforwarded#1
+ {}
+
+%D Normally we tell the users what module is being loaded.
+%D However, the command that is needed for this is not yet
+%D defined.
+%D
+%D \starttyping
+%D \writestatus{laden}{Context Systeem Macros (a)}
+%D \stoptyping
+
+%D The next few macros are needed in case this module is
+%D used outside \CONTEXT.
+
+\ifx\beginTEX\undefined
+ \let\beginTEX\relax\let\endTEX\relax
+ \long\def\beginETEX #1\endETEX {}
+\fi
+
+%D \macros
+%D [protecting]
+%D {protect,unprotect}
+%D
+%D We can shield macros from users by using some special
+%D characters in their names. Some characters that are normally
+%D no letters and therefore often used are: \type{@}, \type{!}
+%D and \type{?}. Before and after the definition of protected
+%D macros, we have to change the \CATCODE\ of these characters.
+%D This is done by \type{\unprotect} and \type{\protect}, for
+%D instance:
+%D
+%D \starttyping
+%D \unprotect
+%D \def\!test{test}
+%D \protect
+%D \stoptyping
+%D
+%D The defined command \type{\!test} can of course only be
+%D called upon when we are in the \type{\unprotect}'ed state,
+%D otherwise \TEX\ reads \type{\!} and probably complains
+%D loudly about not being in math mode.
+%D
+%D Both commands can be used nested, but only the \CATCODE\
+%D of the outermost level is saved. We make use of
+%D an auxilary macro \type{\doprotect} to prevent us from
+%D conflicts with existing macro's \type{\protect}. When
+%D nesting deeper than one level, the system shows the
+%D protection level.
+
+\ifx\protectionlevel\undefined \newcount\protectionlevel \fi
+
+\ifx\protect\undefined
+ \def\protect{\writestatus{protection}{too much protection}}
+\else
+ % a simple version is already defined
+\fi
+
+\let\normalprotect\protect % only for latex
+
+%D Although we don't need the \type{%} after commands that
+%D don't take arguments, unless lines are obeyed, I decided
+%D to put it there as a reminder. I only mention this once.
+
+\ifx\unprotect\undefined
+
+ \chardef\protectionthreshold=10
+
+ \def\saveprotectedcharacters
+ {\edef\doprotectcharacters
+ {\catcode`\noexpand @\the\catcode`@
+ \catcode`\noexpand !\the\catcode`!
+ \catcode`\noexpand ?\the\catcode`? }}
+
+ \def\setprotectedcharacters
+ {\catcode`@=11
+ \catcode`!=11
+ \catcode`?=11 }
+
+ \def\unprotect
+ {\ifcase\protectionlevel
+ \saveprotectedcharacters
+ \let\protect\doprotect
+ \fi
+ \setprotectedcharacters
+ \advance\protectionlevel 1
+ \ifnum\protectionlevel>\protectionthreshold
+ \reportunprotection
+ \fi}
+
+ \def\doprotect
+ {\ifcase\protectionlevel\or
+ \doprotectcharacters
+ \let\doprotectcharacters\relax
+ \let\protect\normalprotect
+ \fi
+ \ifnum\protectionlevel>\protectionthreshold
+ \reportprotection
+ \fi
+ \advance\protectionlevel -1 }
+
+ \def\reportunprotection {\writestatus{protection}{unprotect \protectionstate}}
+ \def\reportprotection {\writestatus{protection}{protect \protectionstate}}
+ \def\reportprotectionstate{\writestatus{protection}{state \protectionstate}}
+
+ \def\protectionstate
+ {\the\protectionlevel
+ \ifcase\protectionthreshold
+ :\space
+ @=\the\catcode`@\space\space
+ !=\the\catcode`!\space\space
+ ?=\the\catcode`?%
+ \fi}
+
+ \ifx\everyeof\undefined
+ \let\checkprotection\relax
+ \else
+ \def\checkprotection{\everyeof{\writestatus{protection}{state: \protectionstate}}}
+ \fi
+
+\else
+
+ \let\reportprotectionstate\relax
+
+\fi
+
+%D Now it is defined, we can make use of this very useful
+%D macro.
+
+\unprotect
+
+%D \macros
+%D {@@escape,@@begingroup,@@endgroup,@@mathshift,@@alignment,
+%D @@endofline,@@parameter,@@superscript,@@subscript,
+%D @@ignore,@@space,@@letter,@@other,@@active,@@comment}
+%D
+%D In \CONTEXT\ we sometimes manipulate the \CATCODES\ of
+%D certain characters. Because we are not that good at numbers,
+%D we introduce some symbolic names.
+
+\chardef\@@escape = 0
+\chardef\@@begingroup = 1
+\chardef\@@endgroup = 2
+\chardef\@@mathshift = 3
+\chardef\@@alignment = 4
+\chardef\@@endofline = 5
+\chardef\@@parameter = 6
+\chardef\@@superscript = 7
+\chardef\@@subscript = 8
+\chardef\@@ignore = 9
+\chardef\@@space = 10
+\chardef\@@letter = 11
+\chardef\@@other = 12 \chardef\other = 12
+\chardef\@@active = 13 \chardef\active = 13
+\chardef\@@comment = 14
+
+%D \macros
+%D {normalspace}
+%D
+%D We often need a space as defined in \PLAIN\ \TEX. Because
+%D we cannot be sure of \type{\space} is redefined, we define:
+
+\def\normalspace{ }
+
+%D \macros
+%D {scratchcounter,
+%D scratchdimen,scratchskip,scratchmuskip,
+%D scratchbox,
+%D scratchtoks}
+%D
+%D Because we often need counters on a temporary basis, we
+%D define the \COUNTER\ \type{\scratchcounter}. This is a
+%D real \COUNTER, and not a pseudo one, as we will meet
+%D further on. We also define some other scratch registers.
+
+\chardef\newabovelimit=20
+
+\def\stripnewabove#1%
+ {\ifnum10<9#1 #1\else\expandafter\stripnewabove\fi}%
+
+\def\newabove#1#2% \dimen \name
+ {#1#2%
+ \ifnum\expandafter\stripnewabove\meaning#2>\newabovelimit\else
+ \expandafter\newabove\expandafter#1\expandafter#2%
+ \fi}
+
+\newabove \newcount \scratchcounter \newabove \newcount \globalscratchcounter
+\newabove \newdimen \scratchdimen \newabove \newdimen \globalscratchdimen
+\newabove \newskip \scratchskip \newabove \newskip \globalscratchskip
+\newabove \newmuskip \scratchmuskip \newabove \newmuskip \globalscratchmuskip
+\newabove \newtoks \scratchtoks \newabove \newtoks \globalscratchtoks
+ \newbox \scratchbox \newbox \globalscratchbox
+
+\newdimen\scratchdimenone \newbox\scratchboxone \newcount\scratchcounterone
+\newdimen\scratchdimentwo \newbox\scratchboxtwo \newcount\scratchcountertwo
+
+%D \macros
+%D {ifdone}
+
+\newif\ifdone
+
+%D \macros
+%D {ifCONTEXT}
+%D
+%D In the system and support modules we sometimes show examples
+%D that make use of core commands. We can skip those parts of
+%D the documentation when we use another macropackage. Of
+%D course we default to false.
+
+\newif \ifCONTEXT
+
+%D \macros
+%D {!!count, !!toks, !!dimen, !!box,
+%D !!width, !!height, !!depth, !!string, !!done}
+%D
+%D We define some more \COUNTERS\ and \DIMENSIONS. We also
+%D define some shortcuts to the local scatchregisters~0, 2, 4,
+%D 6 and~8.
+
+\newcount\!!counta \toksdef\!!toksa=0 \dimendef\!!dimena= 0 \chardef\!!boxa =0
+\newcount\!!countb \toksdef\!!toksb=2 \dimendef\!!dimenb= 2 \chardef\!!boxb =2
+\newcount\!!countc \toksdef\!!toksc=4 \dimendef\!!dimenc= 4 \chardef\!!boxc =4
+\newcount\!!countd \toksdef\!!toksd=6 \dimendef\!!dimend= 6 \chardef\!!boxd =6
+\newcount\!!counte \toksdef\!!tokse=8 \dimendef\!!dimene= 8 \chardef\!!boxe =8
+\newcount\!!countf \dimendef\!!dimenf=10 %skipdef\!!skipa=0
+ \dimendef\!!dimeng=12 %skipdef\!!skipb=2
+ \dimendef\!!dimenh=14 %skipdef\!!skipc=4
+ \dimendef\!!dimeni=16 %skipdef\!!skipd=6
+ \dimendef\!!dimenj=18 %skipdef\!!skipe=8
+ \dimendef\!!dimenk=20 %skipdef\!!skipf=10
+
+\let\!!stringa\empty \let\!!stringb\empty \let\!!stringc\empty
+\let\!!stringd\empty \let\!!stringe\empty \let\!!stringf\empty
+
+\newdimen\!!widtha \newdimen\!!heighta \newdimen\!!deptha
+\newdimen\!!widthb \newdimen\!!heightb \newdimen\!!depthb
+\newdimen\!!widthc \newdimen\!!heightc \newdimen\!!depthc
+
+\newif\if!!donea \newif\if!!doneb \newif\if!!donec
+\newif\if!!doned \newif\if!!donee \newif\if!!donef
+
+\ifx\data\undefined \else \let\data \relax \fi % dep checker
+
+%D Beware: we don't reuse plain counters, too dangerous
+%D when <= 20 (e.g. in supp-pdf this messed up things).
+
+\ifx\undefined\zeroskip \newskip \zeroskip \fi
+\ifx\undefined\zeropoint \newdimen \zeropoint \fi
+\ifx\undefined\zerocount \newcount \zerocount \fi
+\ifx\undefined\minusone \newcount \minusone \fi \minusone = -1
+\ifx\undefined\minustwo \newcount \minustwo \fi \minustwo = -2
+\ifx\undefined\plusone \chardef \plusone = 1 \fi
+\ifx\undefined\plustwo \chardef \plustwo = 2 \fi
+\ifx\undefined\plusthree \chardef \plusthree = 3 \fi
+\ifx\undefined\plusfour \chardef \plusfour = 4 \fi
+\ifx\undefined\plusfive \chardef \plusfive = 5 \fi
+\ifx\undefined\plusten \mathchardef \plusten = 10 \fi
+\ifx\undefined\plushundred \mathchardef \plushundred = 100 \fi
+\ifx\undefined\plusthousand \mathchardef \plusthousand = 1000 \fi
+\ifx\undefined\plustenthousand \mathchardef \plustenthousand = 10000 \fi
+\ifx\undefined\plustwentythousand \mathchardef \plustwentythousand = 20000 \fi
+
+%D \macros
+%D {s!,c!,e!,p!,v!,@@,??}
+%D
+%D To save memory, we use constants (sometimes called
+%D variables). Redefining these constants can have desastrous
+%D results.
+
+\def\v!prefix! {v!} \def\c!prefix! {c!}
+\def\s!prefix! {s!} \def\p!prefix! {p!}
+
+\def\s!next {next} \def\s!default {default}
+\def\s!dummy {dummy} \def\s!unknown {unknown}
+
+\def\s!do {do} \def\s!dodo {dodo}
+
+\def\s!complex {complex} \def\s!start {start}
+\def\s!simple {simple} \def\s!stop {stop}
+
+\def\s!empty {empty}
+
+%D \macros
+%D {@EA,@EAEA,@EAEAEA,@EAEAEAEAEAEA,expanded,startexpanded}
+%D
+%D When in unprotected mode, to be entered with
+%D \type{\unprotect}, one can use \type{\@EA} as equivalent
+%D of \type{\expandafter}.
+
+\let\@NX\noexpand
+\let\@EA\expandafter
+
+\def\@EAEA {\expandafter\expandafter}
+\def\@EAEAEA{\expandafter\expandafter\expandafter}
+
+\def\@EAEAEAEAEAEA{\expandafter\@EAEAEA\expandafter}
+
+%D Sometimes we pass macros as arguments to commands that
+%D don't expand them before interpretation. Such commands can
+%D be enclosed with \type{\expanded}, like:
+%D
+%D \starttyping
+%D \expanded{\setupsomething[\alfa]}
+%D \stoptyping
+%D
+%D Such situations occur for instance when \type{\alfa} is a
+%D commalist or when data stored in macros is fed to index of
+%D list commands. If needed, one should use \type{\noexpand}
+%D inside the argument. Later on we will meet some more clever
+%D alternatives to this command.
+
+\long\def\@@expanded{} % always long; global (less restores)
+
+\long\def\expanded#1%
+ {\long\xdef\@@expanded{\noexpand#1}\@@expanded}
+
+%D Beware, the next one has no \type {\noexpand} before its
+%D argument.
+
+\long\def\startexpanded#1\stopexpanded % see x-fo for example
+ {\long\xdef\@@expanded{#1}\@@expanded}
+
+%D \macros
+%D {safeexpanded,everysafeexpanded}
+%D
+%D In addition we provide:
+
+\newtoks\everysafeexpanded
+
+\long\def\safeexpanded#1% why the \noexpand
+ {\begingroup
+ \the\everysafeexpanded\long\xdef\@@expanded{\noexpand#1}%
+ \endgroup
+ \@@expanded}
+
+\def\safeedef#1#2%
+ {\begingroup
+ \the\everysafeexpanded\long\xdef\@@expanded{\noexpand#2}%
+ \endgroup
+ \let#1\@@expanded}
+
+\def\safexdef#1#2%
+ {\begingroup
+ \the\everysafeexpanded\long\xdef\@@expanded{\noexpand#2}%
+ \endgroup
+ \global\let#1\@@expanded}
+
+%D You can append protective measures to the token register if
+%D needed, as we will do later.
+
+%D \macros
+%D {expandoneargafter,expandtwoargsafter}
+%D
+%D These two commands make macros more readable by hiding a
+%D lot of \type {\expandafter}'s. They expand the arguments
+%D after the first command.
+%D
+%D \starttyping
+%D \expandoneargafter \command{\abc}
+%D \expandtwoargsafter\command{\abc}{\def}
+%D \stoptyping
+%D
+%D These commands expect the arguments to be macros.
+
+\def\expandoneargafter #1{\@EA#1\@EA}
+\def\expandtwoargsafter#1#2{\@EA\@EA\@EA#1\@EA\@EA\@EA{\@EA#2\@EA}\@EA}
+
+%D These two do a full expansion:
+
+\def\fullexpandoneargafter #1#2{\long\xdef\@@expanded{\noexpand#1{#2}}\@@expanded}
+\def\fullexpandtwoargsafter#1#2#3{\long\xdef\@@expanded{\noexpand#1{#2}{#3}}\@@expanded}
+
+%D \macros
+%D {gobbleoneargument,gobble...arguments}
+%D
+%D The next set of macros just do nothing, except that they
+%D get rid of a number of arguments.
+
+\long\def\gobbleoneargument #1{}
+\long\def\gobbletwoarguments #1#2{}
+\long\def\gobblethreearguments #1#2#3{}
+\long\def\gobblefourarguments #1#2#3#4{}
+\long\def\gobblefivearguments #1#2#3#4#5{}
+\long\def\gobblesixarguments #1#2#3#4#5#6{}
+\long\def\gobblesevenarguments #1#2#3#4#5#6#7{}
+\long\def\gobbleeightarguments #1#2#3#4#5#6#7#8{}
+\long\def\gobbleninearguments #1#2#3#4#5#6#7#8#9{}
+\long\def\gobbletenarguments #1{\gobbleninearguments}
+
+%D \macros
+%D {doifnextcharelse}
+%D
+%D When we started using \TEX\ in the late eighties, our
+%D first experiences with programming concerned a simple shell
+%D around \LATEX. The commands probably use most at \PRAGMA,
+%D are the itemizing ones. One of those few shell commands took
+%D care of an optional argument, that enabled us to specify
+%D what kind of item symbol we wanted. Without understanding
+%D anything we were able to locate a \LATEX\ macro that could
+%D be used to inspect the next character.
+%D
+%D It's this macro that the ancester of the next one presented
+%D here. It executes one of two actions, dependant of the next
+%D character. Disturbing spaces and line endings, which are
+%D normally interpreted as spaces too, are skipped.
+%D
+%D \starttyping
+%D \doifnextcharelse {karakter} {then ...} {else ...}
+%D \stoptyping
+%D
+%D This macro differs from the original in the use of \type
+%D {\localnext} because we don't want clashes with \type
+%D {\next}.
+
+\long\def\doifnextcharelse#1#2#3% #1 should not be {} !
+ {\let\charactertoken=#1% = needed here
+ \def\!!stringa{#2}%
+ \def\!!stringb{#3}%
+ \futurelet\nexttoken\inspectnextcharacter}
+
+\def\inspectnextcharacter
+ {\ifx\nexttoken\blankspace
+ \@EA\reinspectnextcharacter
+ \else\ifx\nexttoken\charactertoken
+ \@EAEAEA\!!stringa
+ \else
+ \@EAEAEA\!!stringb
+ \fi\fi}
+
+%D Because we will mostly use this macro for testing if the next
+%D character is \type {[}, we also make a slightly faster variant
+%D as it is not uncommon to have tens of thousands of calls to this
+%D test in a run. Of course it also is more convenient to read a
+%D trace then.
+
+\let\nextoptionalcharactertoken=[
+
+\long\def\doifnextoptionalelse#1#2%
+ {\def\nextoptionalcommandyes{#1}%
+ \def\nextoptionalcommandnop{#2}%
+ \futurelet\nexttoken\inspectnextoptionalcharacter}
+
+\def\inspectnextoptionalcharacter
+ {\ifx\nexttoken\blankspace
+ \@EA\reinspectnextoptionalcharacter
+ \else\ifx\nexttoken\nextoptionalcharactertoken
+ \@EAEAEA\nextoptionalcommandyes
+ \else
+ \@EAEAEA\nextoptionalcommandnop
+ \fi\fi}
+
+\let\nextbgroupcharactertoken\bgroup
+
+\long\def\doifnextbgroupelse#1#2%
+ {\def\nextbgroupcommandyes{#1}%
+ \def\nextbgroupcommandnop{#2}%
+ \futurelet\nexttoken\inspectnextbgroupcharacter}
+
+\def\inspectnextbgroupcharacter
+ {\ifx\nexttoken\blankspace
+ \@EA\reinspectnextbgroupcharacter
+ \else\ifx\nexttoken\nextbgroupcharactertoken
+ \@EAEAEA\nextbgroupcommandyes
+ \else
+ \@EAEAEA\nextbgroupcommandnop
+ \fi\fi}
+
+%D This macro uses some auxiliary macros. Although we were able
+%D to program quite complicated things, I only understood these
+%D after rereading the \TEX book. The trick is in using a
+%D command with a one character name. Such commands differ from
+%D the longer ones in the fact that trailing spaces are {\em
+%D not} skipped. This enables us to indirectly define a long
+%D named macro that gobbles a space.
+%D
+%D In the first line we define \type{\blankspace}. Next we
+%D make \type{\:} equivalent to \type{\reinspect...}. This
+%D one||character command is expanded before the next
+%D \type{\def} comes into action. This way the space after
+%D \type{\:} becomes a delimiter of the longer named
+%D \type{\reinspectnextcharacter}. The chain reaction is
+%D visually compatible with the next sequence:
+%D
+%D \starttyping
+%D \expandafter\def\reinspectnextcharacter %
+%D {\futurelet\nexttoken\inspectnextcharacter}
+%D \stoptyping
+%D
+%D However complicated it may look, I'm still glad I stumbled
+%D into this construction. Saving and restoring \type {\:} is
+%D needed when we use \PPCHTEX\ in \LATEX.
+
+\let\next\:
+
+\def\:{\let\blankspace= } \:
+
+\def\:{\reinspectnextcharacter}
+\expandafter\def\: {\futurelet\nexttoken\inspectnextcharacter}
+
+\def\:{\reinspectnextoptionalcharacter}
+\expandafter\def\: {\futurelet\nexttoken\inspectnextoptionalcharacter}
+
+\def\:{\reinspectnextbgroupcharacter}
+\expandafter\def\: {\futurelet\nexttoken\inspectnextbgroupcharacter}
+
+\let\:\next
+
+%D \macros
+%D {setvalue,setgvalue,setevalue,setxvalue,
+%D letvalue,
+%D getvalue,
+%D resetvalue}
+%D
+%D \TEX's primitive \type{\csname} can be used to construct
+%D all kind of commands that cannot be defined with
+%D \type{\def} and \type{\let}. Every macro programmer sooner
+%D or later wants macros like these.
+%D
+%D \starttyping
+%D \setvalue {name}{...} = \def\name{...}
+%D \setgvalue {name}{...} = \gdef\name{...}
+%D \setevalue {name}{...} = \edef\name{...}
+%D \setxvalue {name}{...} = \xdef\name{...}
+%D \letvalue {name}=\... = \let\name=\...
+%D \letgvalue {name}=\... = \global\let\name=\...
+%D \getvalue {name} = \name
+%D \resetvalue {name} = \def\name{}
+%D \stoptyping
+%D
+%D As we will see, \CONTEXT\ uses these commands many times,
+%D which is mainly due to its object oriented and parameter
+%D driven character.
+
+\def\setvalue #1{\expandafter \def\csname#1\endcsname}
+\def\setgvalue #1{\expandafter\gdef\csname#1\endcsname}
+\def\setevalue #1{\expandafter\edef\csname#1\endcsname}
+\def\setxvalue #1{\expandafter\xdef\csname#1\endcsname}
+\def\getvalue #1{\csname#1\endcsname}
+\def\letvalue #1{\expandafter\let\csname#1\endcsname}
+\def\letgvalue #1{\global\expandafter\let\csname#1\endcsname}
+\def\resetvalue #1{\expandafter\let\csname#1\endcsname\empty}
+\def\ignorevalue#1#2{\expandafter\let\csname#1\endcsname\empty}
+
+\def\setuvalue #1{\normalprotected\expandafter \def\csname#1\endcsname}
+\def\setuevalue #1{\normalprotected\expandafter\edef\csname#1\endcsname}
+\def\setugvalue #1{\normalprotected\expandafter\gdef\csname#1\endcsname}
+\def\setuxvalue #1{\normalprotected\expandafter\xdef\csname#1\endcsname}
+
+%D \macros
+%D {globallet,glet}
+%D
+%D In \CONTEXT\ of May 2000 using \type {\globallet}
+%D instead of the two tokens will save us some
+%D $300\times4=1200$ bytes of format file on a 32~bit
+%D system. So:
+
+\def\globallet{\global\let} \let\glet\globallet
+
+%D \macros
+%D {donottest,unexpanded}
+%D
+%D When expansion of a macro gives problems, we can precede it
+%D by \type{\donottest}. It seems that protection is one of the
+%D burdens of developers of packages, so maybe that's why in
+%D \ETEX\ protection is solved in a more robust way.
+%D
+%D Sometimes prefixing the macro with \type{\donottest} leads
+%D to defining an auxiliary macro, like
+%D
+%D \starttyping
+%D \def\dosomecommand {... ... ...}
+%D \def\somecommand {\donottest\dosomecommand}
+%D \stoptyping
+%D
+%D This double definition can be made transparant by using
+%D \type{\unexpanded}, as in:
+%D
+%D \starttyping
+%D \unexpanded\def\somecommand{... ... ...}
+%D \stoptyping
+%D
+%D The protection mechanism uses:
+
+\beginTEX
+
+\def\dontprocesstest#1{==}
+\def\doprocesstest #1{#1}
+
+\let\donottest=\doprocesstest
+
+\endTEX
+
+\beginETEX \detokenize
+
+\def\donottest#1{#1} % {\detokenize{#1}}
+
+\endETEX
+
+%D By the way, we use a placeholder because we don't want
+%D interference when testing on empty strings. Using a
+%D placeholder of 8~characters increases the processing time
+%D of simple \type{\doifelse} tests by about 10 \%. When we
+%D process the test, we have to remove the braces and
+%D therefore explictly gobble \type{#1}.
+
+%D \macros
+%D {honorunexpanded,forceunexpanded}
+%D
+%D The fact that many macros have the same prefix, could have
+%D a negative impact on searching in the hash table. Because
+%D some simple testing does not show differences, we just use:
+%D
+%D \starttyping
+%D \def\unexpanded#1#2%
+%D {\@EA#1\@EA#2\@EA{\@EA\donottest\csname\s!do\string#2\endcsname}%
+%D \@EA#1\csname\s!do\string#2\endcsname}
+%D \stoptyping
+%D
+%D Well, in fact we use the bit more versatile alternative. The
+%D \type {\honorunexpanded} can be used to \type {\string}
+%D the protected command, which by the way is seldom needed
+%D in \CONTEXT.
+
+\beginTEX
+
+\def\dosetunexpanded#1#2%
+ {\@EA#1\@EA{\@EA#2\@EA}%
+ \@EA{\@EA\donottest\csname\s!do\@EA\string\csname#2\endcsname\endcsname}%
+ \@EA#1\@EA{\@EA\s!do\@EA\string\csname#2\endcsname}}
+
+\def\docomunexpanded#1#2%
+ {\@EA#1\@EA#2\@EA{\@EA\donottest\csname\s!do\string#2\endcsname}%
+ \@EA#1\csname\s!do\string#2\endcsname}
+
+\def\unexpanded#1%
+ {\def\dounexpanded
+ {\ifx\next\bgroup
+ \@EA\dosetunexpanded
+ \else
+ \@EA\docomunexpanded
+ \fi#1}%
+ \futurelet\next\dounexpanded}
+
+\def\honorunexpanded% for writing to a file or message
+ {\def\donottest##1{\expandafter\gobblethreearguments\string##1}}
+
+\def\forceunexpanded% for preventing expansion in \xdef
+ {\def\donottest##1%
+ {\expandafter\noexpand\csname\expandafter\gobblefourarguments\string##1\endcsname}}
+
+\def\resetunexpanded%
+ {\let\donottest\doprocesstest}
+
+\endTEX
+
+\beginETEX \protected
+
+\let \unexpanded \normalprotected
+\let \honorunexpanded \empty % \relax
+\let \forceunexpanded \empty % \relax
+\let \resetunexpanded \empty % \relax
+
+\endETEX
+
+%D This one accepts the more direct \type{\def} and cousins
+%D as well as the \CONTEXT\ specific \type{\setvalue} ones.
+%D
+%D And so the definition in our example turns out to be:
+%D
+%D \starttyping
+%D \def\csname do\somecommand\endcsname{... ... ...}
+%D \def\somecommand{\donottest\csname do\somecommand\endcsname}
+%D \stoptyping
+%D
+%D In which \type{do\somecommand} is hidden from the user and
+%D cannot lead to confusion. It's still permitted to define
+%D auxiliary macros like \type{\dosomecommand}.
+%D
+%D When we are going to use e-\TEX, we'll probably end up
+%D redefining some commands, but we can probably keep the
+%D \type{\unexpanded} ones unchanged.
+
+%D \macros
+%D {doifundefined,doifdefined,
+%D doifundefinedelse,doifdefinedelse,
+%D doifalldefinedelse}
+%D
+%D The standard way of testing if a macro is defined is
+%D comparing its meaning with another undefined one, usually
+%D \type{\undefined}. To garantee correct working of the next
+%D set of macros, \type{\undefined} may never be defined!
+%D
+%D \starttyping
+%D \doifundefined {string} {...}
+%D \doifdefined {string} {...}
+%D \doifundefinedelse {string} {then ...} {else ...}
+%D \doifdefinedelse {string} {then ...} {else ...}
+%D \doifalldefinedelse {commalist} {then ...} {else ...}
+%D \stoptyping
+%D
+%D Every macroname that \TEX\ builds gets an entry in the hash
+%D table, which is of limited size. It is expected that e-\TeX\
+%D will offer a less memory||consuming alternative.
+
+%D Although it will probably never be a big problem, it is good
+%D to be aware of the difference between testing on a macro
+%D name to be build by using \type{\csname} and
+%D \type{\endcsname} and testing the \type{\name} directly.
+%D
+%D \starttyping
+%D \expandafter\ifx\csname NameA\endcsname\relax ... \else ... \fi
+%D
+%D \ifx\NameB\undefined ... \else ... \fi
+%D \stoptyping
+%D
+%D I became aware of this when I mistakenly testen the first
+%D one against \type{\undefined}. When \TEX\ build a name using
+%D \type{\csname} it automatically sets it to \type{\relax},
+%D which is definitely not the same as \type{\undefined}. The
+%D quickest way to check these things is asking \TEX\ to show
+%D the meaning of the names:
+%D
+%D \starttyping
+%D \expandafter\show\csname NameA\endcsname
+%D
+%D \show\NameB
+%D \stoptyping
+%D
+%D The main reason why this never will be a big problem is that
+%D when one uses the \type{\csname} way, one probably has to do
+%D with some macroname that always is dealt with that way.
+%D Confusion can however arise when one applies both testing
+%D methods to the same macroname. By the way, the assignment
+%D of \type{\relax} obeys grouping.
+
+%D The first one gets rid of \type{#1}, but still expands to
+%D something and the second one expands to \type{#1}. Because
+%D we accept arguments between \type{{}}, we have to get rid
+%D of one level of braces.
+%D
+%D Our first implementation of \type{\ifundefined} was
+%D straightforward and readable:
+%D
+%D \starttyping
+%D \def\ifundefined#1%
+%D {\expandafter\ifx\csname#1\endcsname\relax}%
+%D
+%D \def\doifundefinedelse#1#2#3%
+%D {\let\donottest=\dontprocesstest
+%D \ifundefined{#1}%
+%D \let\donottest=\doprocesstest#2%
+%D \else
+%D \let\donottest=\doprocesstest#3%
+%D \fi}
+%D
+%D \def\doifdefinedelse#1#2#3%
+%D {\doifundefinedelse{#1}{#3}{#2}}
+%D
+%D \def\doifundefined#1#2%
+%D {\doifundefinedelse{#1}{#2}{}}
+%D
+%D \def\doifdefined#1#2%
+%D {\doifundefinedelse{#1}{}{#2}}
+%D
+%D \def\doifalldefinedelse#1#2#3%
+%D {\begingroup
+%D \donetrue
+%D \def\checkcommand##1%
+%D {\doifundefined{##1}{\donefalse}}%
+%D \processcommalist[#1]\checkcommand
+%D \ifdone
+%D \endgroup#2%
+%D \else
+%D \endgroup#3%
+%D \fi}
+%D \stoptyping
+%D
+%D When this module was optimized, timing showed that the
+%D next alternative can be upto twice as fast, especially when
+%D longer arguments are used. Watch how we reach over the
+%D \type {\else} and \type {\fi}: this way they cannot get
+%D into the way (and we can avoid those \type {\next}
+%D hacks); I started using this method after I did some
+%D speed optimization tests in the xtag modules; the
+%D efficiency of such hacks depends on the length of the
+%D argument etc. etc.)
+
+\beginTEX
+
+\def\ifundefined#1%
+ {\expandafter\ifx\csname#1\endcsname\relax}
+
+\def\p!doifundefined#1%
+ {\let\donottest\dontprocesstest
+ \expandafter\ifx\csname#1\endcsname\relax}
+
+\def\doifundefinedelse#1%
+ {\p!doifundefined{#1}%
+ \let\donottest\doprocesstest\@EA\firstoftwoarguments
+ \else
+ \let\donottest\doprocesstest\@EA\secondoftwoarguments
+ \fi}
+
+\def\doifdefinedelse#1%
+ {\p!doifundefined{#1}%
+ \let\donottest\doprocesstest\@EA\secondoftwoarguments
+ \else
+ \let\donottest\doprocesstest\@EA\firstoftwoarguments
+ \fi}
+
+\def\doifundefined#1%
+ {\p!doifundefined{#1}%
+ \let\donottest\doprocesstest\@EA\firstofoneargument
+ \else
+ \let\donottest\doprocesstest\@EA\gobbleoneargument
+ \fi}
+
+\def\doifdefined#1%
+ {\p!doifundefined{#1}%
+ \let\donottest\doprocesstest\@EA\gobbleoneargument
+ \else
+ \let\donottest\doprocesstest\@EA\firstofoneargument
+ \fi}
+
+\endTEX
+
+\beginETEX \ifcsname
+
+\def\ifundefined#1% ongelukkige naam
+ {\unless\ifcsname#1\endcsname}
+
+\def\p!doifundefined#1%
+ {\edef\p!defined{#1}%
+ \unless\ifcsname\detokenize\@EA{\p!defined}\endcsname}
+
+\def\doifundefinedelse#1%
+ {\edef\p!defined{#1}%
+ \ifcsname\detokenize\@EA{\p!defined}\endcsname
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+\def\doifdefinedelse#1%
+ {\edef\p!defined{#1}%
+ \ifcsname\detokenize\@EA{\p!defined}\endcsname
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\def\doifundefined#1%
+ {\edef\p!defined{#1}%
+ \ifcsname\detokenize\@EA{\p!defined}\endcsname
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\def\doifdefined#1%
+ {\edef\p!defined{#1}%
+ \ifcsname\detokenize\@EA{\p!defined}\endcsname
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\endETEX
+
+%D \macros
+%D {letbeundefined}
+%D
+%D Testing for being undefined comes down to testing on \type
+%D {\relax} when we use \type {\csname}, but when using \type
+%D {\ifx}, we test on being \type {\undefined}! In \ETEX\ we
+%D have \type {\ifcsname} and that way of testing on existance
+%D is not the same as the one described here. Therefore we
+%D introduce:
+
+\beginTEX
+
+\def\letbeundefined#1%
+ {\expandafter\let\csname#1\endcsname\relax}
+
+\endTEX
+
+\beginETEX \undefined
+
+\def\letbeundefined#1% potential stack buildup when used \global
+ {\expandafter\let\csname#1\endcsname\undefined}
+
+\def\localundefine#1% conditional
+ {\ifcsname#1\endcsname\expandafter\let\csname#1\endcsname\undefined\fi}
+\def\globalundefine#1% conditional
+ {\ifcsname#1\endcsname\expandafter\global\let\csname#1\endcsname\undefined\fi}
+
+\endETEX
+
+%D Beware, being \type {\undefined} in \ETEX\ means that the macro
+%D {\em is} defined!
+
+%D Before we start using this variant, we used another one,
+%D which is even a bit faster. This one looked like:
+%D
+%D \starttyping
+%D \def\p!doifundefined%
+%D {\begingroup
+%D \let\donottest=\dontprocesstest
+%D \ifundefined}
+%D
+%D \def\doifundefinedelse#1#2#3%
+%D {\p!doifundefined{#1}%
+%D \endgroup#2%
+%D \else
+%D \endgroup#3%
+%D \fi}
+%D \stoptyping
+%D
+%D A even more previous version used \type{\bgroup} and
+%D \type {\egroup}. In math mode however, \type{$1{x}2$} differs
+%D from \type{$1x2$}. This can been seen when one compares the
+%D output of:
+%D
+%D \starttyping
+%D $\kern10pt\showthe\lastkern$
+%D $\kern10pt{\showthe\lastkern}$
+%D $\kern10pt\begingroup\showthe\lastkern\endgroup$
+%D \stoptyping
+%D
+%D Also in math mode, one can better use \type {\begingroup}
+%D and companion instead of \type {\bgroup}.
+%D
+%D When we were developing the scientific units module, we
+%D encountered different behavior in text and math mode, which
+%D was due to this grouping subtilities. We therefore decided
+%D to use \type{\begingroup} instead of \type{\bgroup}.
+%D Later, when we had optimized some macro's the grouped
+%D solution turned out to be unsafe when typesetting this
+%D documentation, especially when using \type{\globaldefs}.
+%D
+%D We still have to define \type{\doifalldefinedelse}. Watch
+%D the use of grouping, which garantees local use of the
+%D boolean \type{\ifdone}.
+
+\beginTEX
+
+\def\docheckonedefined#1%
+ {\ifundefined{#1}%
+ \donefalse
+ \fi}
+
+\def\doifalldefinedelse#1%
+ {\begingroup
+ \let\donottest\dontprocesstest
+ \donetrue
+ \processcommalist[#1]\docheckonedefined
+ \ifdone
+ \endgroup\let\donottest\doprocesstest
+ \expandafter\firstoftwoarguments
+ \else
+ \endgroup\let\donottest\doprocesstest
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\endTEX
+
+\beginETEX \ifcsname
+
+\def\docheckonedefined#1%
+ {\unless\ifcsname#1\endcsname
+ \donefalse
+ \fi}
+
+\def\doifalldefinedelse#1%
+ {\begingroup
+ \donetrue \processcommalist[#1]\docheckonedefined
+ \ifdone
+ \endgroup\expandafter\firstoftwoarguments
+ \else
+ \endgroup\expandafter\secondoftwoarguments
+ \fi}
+
+\endETEX
+
+%D \macros
+%D {doif,doifelse,doifnot,
+%D donottest}
+%D
+%D Programming in \TEX\ differs from programming in procedural
+%D languages like \MODULA. This means that one --- well, let me
+%D speek for myself --- tries to do the things in the well
+%D known way. Therefore the next set of \type{\ifthenelse}
+%D commands were between the first ones we needed. A few years
+%D later, the opposite became true: when programming in
+%D \MODULA, I sometimes miss handy things like grouping,
+%D runtime redefinition, expansion etc. While \MODULA\ taught
+%D me to structure, \TEX\ taught me to think recursive.
+%D
+%D \starttyping
+%D \doif {string1} {string2} {...}
+%D \doifnot {string1} {string2} {...}
+%D \doifelse {string1} {string2} {then ...}{else ...}
+%D \stoptyping
+%D
+%D When expansion gives problems, we can precede the
+%D troublemaker with \type{\donottest}.
+%D
+%D This implementatie does not use the construction which is
+%D more robust for nested conditionals.
+%D
+%D \starttyping
+%D \ifx\!!stringa\!!stringb
+%D \def\next{#3}%
+%D \else
+%D \def\next{#4}%
+%D \fi
+%D \next
+%D \stoptyping
+%D
+%D In practice, this alternative is at least 20\% slower than
+%D the alternative used here. The few cases in which we
+%D really need the \type{\next} construction, often need some
+%D other precautions and or adaptions too.
+
+\beginTEX
+
+% \long\def\doif#1#2#3%
+% {\let\donottest\dontprocesstest
+% \edef\!!stringa{#1}%
+% \edef\!!stringb{#2}%
+% \let\donottest\doprocesstest
+% \ifx\!!stringa\!!stringb
+% #3%
+% \fi}
+%
+% \long\def\doifnot#1#2#3%
+% {\let\donottest\dontprocesstest
+% \edef\!!stringa{#1}%
+% \edef\!!stringb{#2}%
+% \let\donottest\doprocesstest
+% \ifx\!!stringa\!!stringb
+% \else
+% #3%
+% \fi}
+%
+% \long\def\doifelse#1#2#3#4%
+% {\let\donottest\dontprocesstest
+% \edef\!!stringa{#1}%
+% \edef\!!stringb{#2}%
+% \let\donottest\doprocesstest
+% \ifx\!!stringa\!!stringb
+% #3%
+% \else
+% #4%
+% \fi}
+
+%D Slightly faster on big arguments, as well as \type
+%D {\next} avoiding:
+
+\long\def\doif#1#2%
+ {\let\donottest\dontprocesstest
+ \edef\!!stringa{#1}%
+ \edef\!!stringb{#2}%
+ \let\donottest\doprocesstest
+ \ifx\!!stringa\!!stringb
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\long\def\doifnot#1#2%
+ {\let\donottest\dontprocesstest
+ \edef\!!stringa{#1}%
+ \edef\!!stringb{#2}%
+ \let\donottest\doprocesstest
+ \ifx\!!stringa\!!stringb
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+
+\long\def\doifelse#1#2%
+ {\let\donottest\dontprocesstest
+ \edef\!!stringa{#1}%
+ \edef\!!stringb{#2}%
+ \let\donottest\doprocesstest
+ \ifx\!!stringa\!!stringb
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\endTEX
+
+\beginETEX \protected
+
+% \long\def\doif#1#2#3%
+% {\edef\!!stringa{#1}\edef\!!stringb{#2}%
+% \ifx\!!stringa\!!stringb#3\fi}
+%
+% \long\def\doifnot#1#2#3%
+% {\edef\!!stringa{#1}\edef\!!stringb{#2}%
+% \unless\ifx\!!stringa\!!stringb#3\fi}
+%
+% \long\def\doifelse#1#2#3#4%
+% {\edef\!!stringa{#1}\edef\!!stringb{#2}%
+% \ifx\!!stringa\!!stringb#3\else#4\fi}
+
+%D Slightly faster on big arguments, as well as \type
+%D {\next} avoiding:
+
+\long\def\doif#1#2%
+ {\edef\!!stringa{#1}\edef\!!stringb{#2}%
+ \ifx\!!stringa\!!stringb
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\long\def\doifnot#1#2%
+ {\edef\!!stringa{#1}\edef\!!stringb{#2}%
+ \ifx\!!stringa\!!stringb
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\long\def\doifelse#1#2%
+ {\edef\!!stringa{#1}\edef\!!stringb{#2}%
+ \ifx\!!stringa\!!stringb
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\endETEX
+
+%D One could wonder why we don't follow the the same approach
+%D as in \type{\doifdefined} c.s.\ and use \type{\begingroup}
+%D and \type{\endgroup}. In this case, this alternative is
+%D slower, which is probably due to the fact that more meanings
+%D need to be restored.
+%D
+%D The in terms of memory more efficient alternative using a
+%D auxiliary macro also proved to be slower, so we definitely
+%D did not choose for:
+%D
+%D \starttyping
+%D \def\p!doifelse#1#2%
+%D {\let\donottest=\dontprocesstest
+%D \edef\!!stringa{#1}%
+%D \edef\!!stringb{#2}%
+%D \let\donottest=\doprocesstest
+%D \ifx\!!stringa\!!stringb}
+%D
+%D \long\def\doif#1#2#3%
+%D {\p!doifelse{#1}{#2}#3\fi}
+%D
+%D \long\def\doifnot#1#2#3%
+%D {\p!doifelse{#1}{#2}\else#3\fi}
+%D
+%D \long\def\doifelse#1#2#3#4%
+%D {\p!doifelse{#1}{#2}#3\else#4\fi}
+%D \stoptyping
+%D
+%D Optimizations like this are related of course to the
+%D bottlenecks in \TEX. It seems that restoring saved meanings
+%D and passing arguments takes some time.
+
+%D \macros
+%D {doifempty,doifemptyelse,doifnotempty}
+%D
+%D We complete our set of conditionals with:
+%D
+%D \starttyping
+%D \doifempty {string} {...}
+%D \doifnotempty {string} {...}
+%D \doifemptyelse {string} {then ...} {else ...}
+%D \stoptyping
+%D
+%D This time, the string is not expanded.
+
+\long\def\doifemptyelse#1%
+ {\def\!!stringa{#1}%
+ \ifx\!!stringa\empty
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\long\def\doifempty#1%
+ {\def\!!stringa{#1}%
+ \ifx\!!stringa\empty
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\long\def\doifnotempty#1%
+ {\def\!!stringa{#1}%
+ \ifx\!!stringa\empty
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+%D \macros
+%D {doifinset,doifnotinset,doifinsetelse}
+%D
+%D We can check if a string is present in a comma separated
+%D set of strings. Depending on the result, some action is
+%D taken.
+%D
+%D \starttyping
+%D \doifinset {string} {string,...} {...}
+%D \doifnotinset {string} {string,...} {...}
+%D \doifinsetelse {string} {string,...} {then ...} {else ...}
+%D \stoptyping
+%D
+%D The second argument is the comma separated set of strings.
+%D
+%D \starttyping
+%D \long\def\doifinsetelse#1#2#3#4%
+%D {\doifelse{#1}{}
+%D {#4}
+%D {\donefalse
+%D \def\p!checkiteminset##1%
+%D {\doif{#1}{##1}
+%D {\donetrue
+%D \let\p!checkiteminset=\gobbleoneargument}}%
+%D \processcommalist[#2]\p!checkiteminset
+%D \ifdone
+%D #3%
+%D \else
+%D #4%
+%D \fi}}
+%D
+%D \long\def\doifinset#1#2#3%
+%D {\doifinsetelse{#1}{#2}{#3}{}}
+%D
+%D \long\def\doifnotinset#1#2#3%
+%D {\doifinsetelse{#1}{#2}{}{#3}}
+%D \stoptyping
+%D
+%D Because this macro is called quite often we've spent some
+%D time optimizing it. This time, the gain in speed is due to
+%D (1)~defining an external auxiliary macro, (2)~not calling
+%D any other macros and (3)~minimizing the passing of
+%D arguments. The gain in speed is impressive.
+
+% \def\p!dodocheckiteminset#1%
+% {\edef\!!stringb{#1}%
+% \ifx\!!stringa\!!stringb
+% \donetrue
+% \let\p!docheckiteminset\gobbleoneargument
+% \fi}
+%
+% \beginTEX
+%
+% \def\p!doifinsetelse#1#2%
+% {\let\donottest\dontprocesstest
+% \donefalse
+% \edef\!!stringa{#1}%
+% \ifx\!!stringa\empty
+% \else
+% \let\p!docheckiteminset\p!dodocheckiteminset
+% \processcommalist[#2]\p!docheckiteminset
+% \fi
+% \let\donottest\doprocesstest
+% \ifdone}
+%
+% \endTEX
+%
+% \beginETEX \protected
+%
+% \def\p!doifinsetelse#1#2%
+% {\donefalse
+% \edef\!!stringa{#1}%
+% \ifx\!!stringa\empty
+% \else
+% \let\p!docheckiteminset\p!dodocheckiteminset
+% \processcommalist[#2]\p!docheckiteminset
+% \fi
+% \ifdone}
+%
+% \endETEX
+
+% then we had:
+%
+% \def\p!docheckiteminset#1%
+% {\edef\!!stringb{#1}%
+% \ifx\!!stringa\!!stringb
+% \donetrue
+% \expandafter\quitcommalist
+% \fi}
+%
+% \beginTEX
+%
+% \def\p!doifinsetelse#1#2%
+% {\let\donottest\dontprocesstest
+% \donefalse
+% \edef\!!stringa{#1}%
+% \ifx\!!stringa\empty
+% \else
+% \processcommalist[#2]\p!docheckiteminset
+% \fi
+% \let\donottest\doprocesstest
+% \ifdone}
+%
+% \endTEX
+%
+% % can be sped up with processnext...
+%
+% \beginETEX \protected
+%
+% \def\p!doifinsetelse#1#2%
+% {\donefalse
+% \edef\!!stringa{#1}%
+% \ifx\!!stringa\empty
+% \else
+% \processcommalist[#2]\p!docheckiteminset
+% \fi
+% \ifdone}
+%
+% \endETEX
+%
+% \long\def\doifinsetelse#1#2%
+% {\p!doifinsetelse{#1}{#2}%
+% \expandafter\firstoftwoarguments
+% \else
+% \expandafter\secondoftwoarguments
+% \fi}
+%
+% \long\def\doifinset#1#2%
+% {\p!doifinsetelse{#1}{#2}%
+% \expandafter\firstofoneargument
+% \else
+% \expandafter\gobbleoneargument
+% \fi}
+%
+% \long\def\doifnotinset#1#2%
+% {\p!doifinsetelse{#1}{#2}%
+% \expandafter\gobbleoneargument
+% \else
+% \expandafter\firstofoneargument
+% \fi}
+%
+% now we have
+
+\def\p!docheckiteminset#1%
+ {\edef\!!stringb{#1}%
+ \ifx\!!stringa\!!stringb
+ \donetrue
+ \expandafter\quitcommalist
+ \fi}
+
+\beginTEX
+
+\def\p!doifinsetelse#1#2#3#4%
+ {\let\donottest\dontprocesstest
+ \donefalse
+ \edef\!!stringa{#3}%
+ \ifx\!!stringa\empty
+ \else
+ \processcommalist[#4]\p!docheckiteminset
+ \fi
+ \let\donottest\doprocesstest
+ \ifdone\expandafter#1\else\expandafter#2\fi}
+
+\endTEX
+
+\beginETEX \protected
+
+\def\p!doifinsetelse#1#2#3#4%
+ {\donefalse
+ \edef\!!stringa{#3}%
+ \ifx\!!stringa\empty
+ \else
+ \processcommalist[#4]\p!docheckiteminset
+ \fi
+ \ifdone\expandafter#1\else\expandafter#2\fi}
+
+\endETEX
+
+\long\def\doifinsetelse
+ {\p!doifinsetelse\firstoftwoarguments\secondoftwoarguments}
+
+\long\def\doifinset
+ {\p!doifinsetelse\firstofoneargument\gobbleoneargument}
+
+\long\def\doifnotinset
+ {\p!doifinsetelse\gobbleoneargument\firstofoneargument}
+
+%D \macros
+%D {doifcommon,doifnotcommon,doifcommonelse}
+%D
+%D Probably the most time consuming tests are those that test
+%D for overlap in sets of strings.
+%D
+%D \starttyping
+%D \doifcommon {string,...} {string,...} {...}
+%D \doifnotcommon {string,...} {string,...} {...}
+%D \doifcommonelse {string,...} {string,...} {then ...} {else ...}
+%D \stoptyping
+%D
+%D We show the slower alternative first, because it shows us
+%D how things are done.
+%D
+%D \starttyping
+%D \long\def\doifcommonelse#1#2#3#4%
+%D {\donefalse
+%D \def\p!docommoncheck##1%
+%D {\def\p!dodocommoncheck####1%
+%D {\doif{####1}{##1}
+%D {\donetrue
+%D \def\commalistelement{##1}%
+%D \let\p!docommoncheck=\gobbleoneargument
+%D \let\p!dodocommoncheck=\gobbleoneargument}}%
+%D \processcommalist[#2]\p!dodocommoncheck}%
+%D \processcommalist[#1]\p!docommoncheck
+%D \ifdone
+%D #3%
+%D \else
+%D #4%
+%D \fi}
+%D
+%D \long\def\doifcommon#1#2#3%
+%D {\doifcommonelse{#1}{#2}{#3}{}}
+%D
+%D \long\def\doifnotcommon#1#2#3%
+%D {\doifcommonelse{#1}{#2}{}{#3}}
+%D \stoptyping
+%D
+%D The processing time is shortened by getting the auxiliary
+%D macro to the outermost level and using less \type{\edef}'s.
+%D Sometimes it makes more sence to define local macro's not
+%D only because this way we can be sure that they are not
+%D redefined, but also because it shows the dependance. In
+%D compiled languages, this is no problem at all. It can even
+%D save us bytes and processing time. In interpreted languages
+%D like \TEX\ it nearly always slows down processing.
+
+% \def\p!dododocommoncheck#1%
+% {\edef\!!stringb{#1}%
+% \ifx\!!stringa\!!stringb
+% \donetrue
+% \let\p!docommoncheck\gobbleoneargument
+% \let\p!dodocommoncheck\gobbleoneargument
+% \fi}
+%
+% \beginTEX
+%
+% \def\p!doifcommonelse#1#2%
+% {\donefalse
+% \let\donottest\dontprocesstest
+% \let\p!dodocommoncheck\p!dododocommoncheck
+% \def\p!docommoncheck##1%
+% {\edef\!!stringa{##1}%
+% \def\commalistelement{##1}%
+% \processcommalist[#2]\p!dodocommoncheck}%
+% \processcommalist[#1]\p!docommoncheck
+% \let\donottest\doprocesstest
+% \ifdone}
+%
+% \endTEX
+%
+% \beginETEX \protected
+%
+% \def\p!doifcommonelse#1#2%
+% {\donefalse
+% \let\p!dodocommoncheck\p!dododocommoncheck
+% \def\p!docommoncheck##1%
+% {\edef\!!stringa{##1}%
+% \def\commalistelement{##1}%
+% \processcommalist[#2]\p!dodocommoncheck}%
+% \processcommalist[#1]\p!docommoncheck
+% \ifdone}
+%
+% \endETEX
+
+% \def\p!dodocommoncheck#1%
+% {\edef\!!stringb{#1}%
+% \ifx\!!stringa\!!stringb
+% \donetrue
+% \expandafter\quitprevcommalist
+% \fi}
+%
+% \beginTEX
+%
+% \def\p!doifcommonelse#1#2%
+% {\donefalse
+% \let\donottest\dontprocesstest
+% \def\p!docommoncheck##1%
+% {\edef\!!stringa{##1}%
+% \def\commalistelement{##1}% no let to stringa
+% \processcommalist[#2]\p!dodocommoncheck}%
+% \processcommalist[#1]\p!docommoncheck
+% \let\donottest\doprocesstest
+% \ifdone}
+%
+% \endTEX
+%
+% \beginETEX \protected
+%
+% \def\p!doifcommonelse#1#2%
+% {\donefalse
+% \def\p!docommoncheck##1%
+% {\edef\!!stringa{##1}%
+% \def\commalistelement{##1}%
+% \processcommalist[#2]\p!dodocommoncheck}%
+% \processcommalist[#1]\p!docommoncheck
+% \ifdone}
+%
+% \endETEX
+%
+% \long\def\doifcommonelse#1#2% % #3#4%
+% {\p!doifcommonelse{#1}{#2}% % #3\else#4\fi}
+% \expandafter\firstoftwoarguments
+% \else
+% \expandafter\secondoftwoarguments
+% \fi}
+%
+% \long\def\doifcommon#1#2%
+% {\p!doifcommonelse{#1}{#2}%
+% \expandafter\firstofoneargument
+% \else
+% \expandafter\gobbleoneargument
+% \fi}
+%
+% \long\def\doifnotcommon#1#2%
+% {\p!doifcommonelse{#1}{#2}%
+% \expandafter\gobbleoneargument
+% \else
+% \expandafter\firstofoneargument
+% \fi}
+
+% todo: use dedicated done
+
+\def\p!dodocommoncheck#1%
+ {\edef\!!stringb{#1}%
+ \ifx\!!stringa\!!stringb
+ \donetrue
+ \expandafter\quitprevcommalist
+ \fi}
+
+\beginTEX
+
+\def\p!doifcommonelse#1#2#3#4%
+ {\donefalse
+ \let\donottest\dontprocesstest
+ \def\p!docommoncheck##1%
+ {\edef\!!stringa{##1}%
+ \def\commalistelement{##1}% no let to stringa
+ \processcommalist[#4]\p!dodocommoncheck}%
+ \processcommalist[#3]\p!docommoncheck
+ \let\donottest\doprocesstest
+ \ifdone\expandafter#1\else\expandafter#2\fi}
+
+\endTEX
+
+\beginETEX \protected
+
+\def\p!doifcommonelse#1#2#3#4%
+ {\donefalse
+ \def\p!docommoncheck##1%
+ {\edef\!!stringa{##1}%
+ \def\commalistelement{##1}%
+ \processcommalist[#4]\p!dodocommoncheck}%
+ \processcommalist[#3]\p!docommoncheck
+ \ifdone\expandafter#1\else\expandafter#2\fi}
+
+\endETEX
+
+\def\doifcommonelse
+ {\p!doifcommonelse\firstoftwoarguments\secondoftwoarguments}
+
+\def\doifcommon
+ {\p!doifcommonelse\firstofoneargument \gobbleoneargument}
+
+\def\doifnotcommon
+ {\p!doifcommonelse\gobbleoneargument \firstofoneargument}
+
+%D \macros
+%D {processcommalist,processcommacommand,quitcommalist,
+%D processcommalistwithparameters}
+%D
+%D We've already seen some macros that take care of comma
+%D separated lists. Such list can be processed with
+%D
+%D \starttyping
+%D \processcommalist[string,string,...]\commando
+%D \stoptyping
+%D
+%D The user supplied command \type{\commando} receives one
+%D argument: the string. This command permits nesting and
+%D spaces after commas are skipped. Empty sets are no problem.
+%D
+%D \startbuffer
+%D \def\dosomething#1{(#1)}
+%D
+%D 1: \processcommalist [\hbox{$a,b,c,d,e,f$}] \dosomething \par
+%D 2: \processcommalist [{a,b,c,d,e,f}] \dosomething \par
+%D 3: \processcommalist [{a,b,c},d,e,f] \dosomething \par
+%D 4: \processcommalist [a,b,{c,d,e},f] \dosomething \par
+%D 5: \processcommalist [a{b,c},d,e,f] \dosomething \par
+%D 6: \processcommalist [{a,b}c,d,e,f] \dosomething \par
+%D 7: \processcommalist [] \dosomething \par
+%D 8: \processcommalist [{[}] \dosomething \par
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D Before we show the result, we present the macro's:
+
+\newcount\commalevel
+
+\def\dododoprocesscommaitem
+ {\csname\s!next\the\commalevel\endcsname}
+
+%\def\dodoprocesscommaitem%
+% {\ifx\nexttoken\blankspace
+% \let\nextcommaitem\redoprocesscommaitem
+% \else\ifx\nexttoken]%
+% \let\nextcommaitem\gobbleoneargument
+% \else
+% \let\nextcommaitem\dododoprocesscommaitem
+% \fi\fi
+% \nextcommaitem}
+%
+% faster ?
+
+\def\dodoprocesscommaitem
+ {\ifx\nexttoken\blankspace
+ \@EA\redoprocesscommaitem
+ \else\ifx\nexttoken]%
+ \@EAEAEA\gobbleoneargument
+ \else
+ \@EAEAEA\dododoprocesscommaitem
+ \fi\fi}
+
+\def\doprocesscommaitem
+ {\futurelet\nexttoken\dodoprocesscommaitem}
+
+%D Empty arguments are not processed. Empty items (\type{,,})
+%D however are treated. We have to check for the special case
+%D \type{[{a,b,c}]}.
+%D
+%D \starttyping
+%D \def\processcommalist[%
+%D {\futurelet\nexttoken\docheckcommaitem}
+%D
+%D \def\docheckcommaitem%
+%D {\ifx\nexttoken]%
+%D \let\nextcommaitem\gobbletwoarguments
+%D \else\ifx\nexttoken\bgroup
+%D \let\nextcommaitem\doprocesscommalistA
+%D \else
+%D \let\nextcommaitem\doprocesscommalistB
+%D \fi\fi
+%D \nextcommaitem}
+%D
+%D \def\doprocesscommalistA#1#2]#3%
+%D {\global\advance\commalevel 1
+%D \long\expandafter\def\csname\s!next\the\commalevel\endcsname##1,%
+%D {#3{##1}\doprocesscommaitem}%
+%D \doprocesscommaitem{#1}#2,]\relax
+%D \global\advance\commalevel -1 }
+%D
+%D \def\doprocesscommalistB#1]#2%
+%D {\global\advance\commalevel 1
+%D \long\expandafter\def\csname\s!next\the\commalevel\endcsname##1,%
+%D {#2{##1}\doprocesscommaitem}%
+%D \doprocesscommaitem#1,]\relax
+%D \global\advance\commalevel -1 }
+%D \stoptyping
+%D
+%D However, this is not a the most straightforward solution!
+%D We can misuse one of \TEX's hidden features, and prepend
+%D and remove a \type {\relax}. By the way, although it
+%D involves less testing, this cleaner alternative is not
+%D faster.
+
+\def\processcommalist[%
+ {\futurelet\nexttoken\docheckcommaitem}
+
+\def\docheckcommaitem
+ {\ifx\nexttoken]%
+ \expandafter\gobblethreearguments
+ \else
+ \expandafter\doprocesscommalist
+ \fi
+ \relax} % this one preserved the next {}
+
+\def\doprocesscommalist#1]#2%
+ {\global\advance\commalevel \plusone
+ \long\expandafter\def\csname\s!next\the\commalevel\endcsname##1,%
+ {#2{##1}\doprocesscommaitem}%
+ \@EA\dodoprocesscommaitem\gobbleoneargument#1,]\relax
+ \global\advance\commalevel \minusone }
+
+%D One way of quitting a commalist halfway is:
+
+\def\quitcommalist
+ {\begingroup\let\doprocesscommaitem\doquitcommalist}
+
+\def\doquitcommalist#1]%
+ {\endgroup}
+
+\def\quitprevcommalist
+ {\begingroup\let\doprocesscommaitem\doquitprevcommalist}
+
+\def\doquitprevcommalist#1]%
+ {\let\doprocesscommaitem\doquitcommalist}
+
+%D The hack we used for checking the next character
+%D \type {\doifnextcharelse} is also used here.
+
+\def\:{\redoprocesscommaitem}
+
+\expandafter\def\: {\futurelet\nexttoken\dodoprocesscommaitem}
+
+%D The previous examples lead to:
+%D
+%D \getbuffer
+
+%D When a list is saved in a macro, we can use a construction
+%D like:
+%D
+%D \starttyping
+%D \expandafter\processcommalist\expandafter[\list]\command
+%D \stoptyping
+%D
+%D Such solutions suit most situations, but we wanted a bit
+%D more.
+%D
+%D \starttyping
+%D \processcommacommand[string,\stringset,string]\commando
+%D \stoptyping
+%D
+%D where \type{\stringset} is a predefined set, like:
+%D
+%D \starttyping
+%D \def\first{aap,noot,mies}
+%D \def\second{laatste}
+%D
+%D \processcommacommand[\first]\message
+%D \processcommacommand[\first,second,third]\message
+%D \processcommacommand[\first,between,\second]\message
+%D \stoptyping
+%D
+%D Commands that are part of the list are expanded, so the
+%D use of this macro has its limits.
+
+\def\processcommacommand[#1]%
+ {\expanded{\processcommalist[#1]}}
+
+%D The argument to \type{\command} is not delimited. Because
+%D we often use \type{[]} as delimiters, we also have:
+%D
+%D \starttyping
+%D \processcommalistwithparameters[string,string,...]\command
+%D \stoptyping
+%D
+%D where \type{\command} looks like:
+%D
+%D \starttyping
+%D \def\command[#1]{... #1 ...}
+%D \stoptyping
+
+\def\processcommalistwithparameters[#1]#2%
+ {\def\docommand##1{#2[##1]}%
+ \processcommalist[#1]\docommand}
+
+%D \macros
+%D {processaction,
+%D processfirstactioninset,
+%D processallactionsinset}
+%D
+%D \CONTEXT\ makes extensive use of a sort of case or switch
+%D command. Depending of the presence of one or more provided
+%D items, some actions is taken. These macros can be nested
+%D without problems.
+%D
+%D \starttyping
+%D \processaction [x] [a=>\a,b=>\b,c=>\c]
+%D \processfirstactioninset [x,y,z] [a=>\a,b=>\b,c=>\c]
+%D \processallactionsinset [x,y,z] [a=>\a,b=>\b,c=>\c]
+%D \stoptyping
+%D
+%D We can supply both a \type{default} action and an action
+%D to be undertaken when an \type{unknown} value is met:
+%D
+%D \starttyping
+%D \processallactionsinset
+%D [x,y,z]
+%D [ a=>\a,
+%D b=>\b,
+%D c=>\c,
+%D default=>\default,
+%D unknown=>\unknown{... \commalistelement ...}]
+%D \stoptyping
+%D
+%D When \type{#1} is empty, this macro scans list \type{#2} for
+%D the keyword \type{default} and executed the related action
+%D if present. When \type{#1} is non empty and not in the list,
+%D the action related to \type{unknown} is executed. Both
+%D keywords must be at the end of list \type{#2}. Afterwards,
+%D the actually found keyword is available in
+%D \type{\commalistelement}. An advanced example of the use of
+%D this macro can be found in \PPCHTEX, where we completely
+%D rely on \TEX\ for interpreting user supplied keywords like
+%D \type{SB}, \type{SB1..6}, \type{SB125} etc.
+%D
+%D Even a quick glance at the macros below show some overlap,
+%D which means that more efficient alternatives are possible.
+%D Because these macro's are very sensitive to subtle changes,
+%D we've decided to present the readable originals first
+%D Maybe these these macros look complicated, but this is a
+%D direct result of the support of nesting. Protection is only
+%D applied in \type{\processaction}.
+%D
+%D \starttyping
+%D \newcount\processlevel
+%D
+%D \def\processaction[#1]#2[#3]%
+%D {\doifelse{#1}{}
+%D {\def\p!compareprocessaction[##1=>##2]%
+%D {\edef\!!stringa{##1}%
+%D \ifx\!!stringa\s!default
+%D \def\commalistelement{#1}%
+%D ##2%
+%D \fi}}
+%D {\let\donottest=\dontprocesstest
+%D \edef\!!stringb{#1}%
+%D \let\donottest=\doprocesstest
+%D \def\p!compareprocessaction[##1=>##2]%
+%D {\edef\!!stringa{##1}%
+%D \ifx\!!stringa\!!stringb
+%D \def\commalistelement{#1}%
+%D ##2%
+%D \let\p!doprocessaction=\gobbleoneargument
+%D \else\ifx\!!stringa\s!unknown
+%D \def\commalistelement{#1}%
+%D ##2%
+%D \fi\fi}}%
+%D \def\p!doprocessaction##1%
+%D {\p!compareprocessaction[##1]}%
+%D \processcommalist[#3]\p!doprocessaction}
+%D
+%D \def\processfirstactioninset[#1]#2[#3]%
+%D {\doifelse{#1}{}
+%D {\processaction[][#3]}
+%D {\def\p!compareprocessaction[##1=>##2][##3]%
+%D {\edef\!!stringa{##1}%
+%D \edef\!!stringb{##3}%
+%D \ifx\!!stringa\!!stringb
+%D \def\commalistelement{##3}%
+%D ##2%
+%D \let\p!doprocessaction=\gobbleoneargument
+%D \let\p!dodoprocessaction=\gobbleoneargument
+%D \else\ifx\!!stringa\s!unknown
+%D \def\commalistelement{##3}%
+%D ##2%
+%D \fi\fi}%
+%D \def\p!doprocessaction##1%
+%D {\def\p!dodoprocessaction####1%
+%D {\p!compareprocessaction[####1][##1]}%
+%D \processcommalist[#3]\p!dodoprocessaction}%
+%D \processcommalist[#1]\p!doprocessaction}}
+%D
+%D \def\processallactionsinset[#1]#2[#3]%
+%D {\doifelse{#1}{}
+%D {\processaction[][#3]}
+%D {\advance\processlevel by 1
+%D \def\p!compareprocessaction[##1=>##2][##3]%
+%D {\edef\!!stringa{##1}%
+%D \edef\!!stringb{##3}%
+%D \ifx\!!stringa\!!stringb
+%D \def\commalistelement{##3}%
+%D ##2%
+%D \let\p!dodoprocessaction=\gobbleoneargument
+%D \else\ifx\!!stringa\s!unknown
+%D \def\commalistelement{##3}%
+%D ##2%
+%D \fi\fi}%
+%D \setvalue{\s!do\the\processlevel}##1%
+%D {\def\p!dodoprocessaction####1%
+%D {\p!compareprocessaction[####1][##1]}%
+%D \processcommalist[#3]\p!dodoprocessaction}%
+%D \processcommalist[#1]{\getvalue{\s!do\the\processlevel}}%
+%D \advance\processlevel by -1 }}
+%D \stoptyping
+%D
+%D The gain of speed in the (again) next implementation is
+%D around 20\%, depending on the application.
+
+\newcount\processlevel
+
+\def\p!compareprocessactionA[#1=>#2][#3]%
+ {\edef\!!stringb{#1}%
+ \ifx\!!stringb\s!default
+ \let\commalistelement\empty
+ #2%
+ \fi}
+
+% \def\p!compareprocessactionB[#1=>#2][#3]%
+% {\expandedaction\!!stringb{#1}%
+% \ifx\!!stringa\!!stringb
+% \def\commalistelement{#3}%
+% #2%
+% \let\p!doprocessaction\gobbleoneargument
+% \else
+% \edef\!!stringb{#1}%
+% \ifx\!!stringb\s!unknown
+% \def\commalistelement{#3}% beware of loops
+% #2%
+% \fi
+% \fi}
+
+% met \quitcommalist tot meer dan 25\% sneller
+
+\def\p!compareprocessactionB[#1=>#2][#3]%
+ {\expandedaction\!!stringb{#1}%
+ \ifx\!!stringa\!!stringb
+ \def\commalistelement{#3}%
+ #2%
+ \expandafter\quitcommalist
+ \else
+ \edef\!!stringb{#1}%
+ \ifx\!!stringb\s!unknown
+ \def\commalistelement{#3}% beware of loops
+ #2%
+ \fi
+ \fi}
+
+\beginTEX
+
+\def\processaction[#1]#2[#3]%
+ {\let\donottest\dontprocesstest
+ \expandedaction\!!stringa{#1}%
+ \let\donottest\doprocesstest
+ \ifx\!!stringa\empty
+ \let\p!compareprocessaction\p!compareprocessactionA
+ \else
+ \let\p!compareprocessaction\p!compareprocessactionB
+ \fi
+ \def\p!doprocessaction##1%
+ {\p!compareprocessaction[##1][#1]}%
+ \processcommalist[#3]\p!doprocessaction
+ \expandactions}
+
+\endTEX
+
+\beginETEX \protected
+
+\def\processaction[#1]#2[#3]% faster version follows
+ {\expandedaction\!!stringa{#1}%
+ \ifx\!!stringa\empty
+ \let\p!compareprocessaction\p!compareprocessactionA
+ \else
+ \let\p!compareprocessaction\p!compareprocessactionB
+ \fi
+ \def\p!doprocessaction##1%
+ {\p!compareprocessaction[##1][#1]}%
+ \processcommalist[#3]\p!doprocessaction
+ \expandactions}
+
+\endETEX
+
+% \def\p!compareprocessactionC[#1=>#2][#3]%
+% {\expandedaction\!!stringa{#1}%
+% \expandedaction\!!stringb{#3}%
+% \ifx\!!stringa\!!stringb
+% \def\commalistelement{#3}%
+% #2%
+% \let\p!doprocessaction\gobbleoneargument
+% \let\p!dodoprocessaction\gobbleoneargument
+% \else
+% \edef\!!stringa{#1}%
+% \ifx\!!stringa\s!unknown
+% \def\commalistelement{#3}%
+% #2%
+% \fi
+% \fi}
+
+\def\p!compareprocessactionC[#1=>#2][#3]%
+ {\expandedaction\!!stringa{#1}%
+ \expandedaction\!!stringb{#3}%
+ \ifx\!!stringa\!!stringb
+ \def\commalistelement{#3}%
+ #2%
+ \expandafter\quitprevcommalist
+ \else
+ \edef\!!stringa{#1}%
+ \ifx\!!stringa\s!unknown
+ \def\commalistelement{#3}%
+ #2%
+ \fi
+ \fi}
+
+\def\processfirstactioninset[#1]#2[#3]% faster version follows
+ {\expandedaction\!!stringa{#1}%
+ \ifx\!!stringa\empty
+ \processaction[][#3]%
+ \else
+ \def\p!doprocessaction##1%
+ {\def\p!dodoprocessaction####1%
+ {\p!compareprocessactionC[####1][##1]}%
+ \processcommalist[#3]\p!dodoprocessaction}%
+ \processcommalist[#1]\p!doprocessaction
+ \fi
+ \expandactions}
+
+% \def\p!compareprocessactionD[#1=>#2][#3]%
+% {\expandedaction\!!stringa{#1}%
+% \expandedaction\!!stringb{#3}%
+% \ifx\!!stringa\!!stringb
+% \def\commalistelement{#3}%
+% #2%
+% \let\p!dodoprocessaction\gobbleoneargument
+% \else
+% \edef\!!stringa{#1}%
+% \ifx\!!stringa\s!unknown
+% \def\commalistelement{#3}%
+% #2%
+% \fi
+% \fi}
+
+\def\p!compareprocessactionD[#1=>#2][#3]%
+ {\expandedaction\!!stringa{#1}%
+ \expandedaction\!!stringb{#3}%
+ \ifx\!!stringa\!!stringb
+ \def\commalistelement{#3}%
+ #2%
+ \expandafter\quitcommalist
+ \else
+ \edef\!!stringa{#1}%
+ \ifx\!!stringa\s!unknown
+ \def\commalistelement{#3}%
+ #2%
+ \fi
+ \fi}
+
+\def\doprocessallactionsinset
+ {\csname\s!do\the\processlevel\endcsname}
+
+\def\processallactionsinset[#1]#2[#3]% faster version follows
+ {\expandedaction\!!stringa{#1}%
+ \ifx\!!stringa\empty
+ \processaction[][#3]%
+ \else
+ \advance\processlevel \plusone
+ \expandafter\def\csname\s!do\the\processlevel\endcsname##1%
+ {\def\p!dodoprocessaction####1%
+ {\p!compareprocessactionD[####1][##1]}%
+ \processcommalist[#3]\p!dodoprocessaction}%
+ \processcommalist[#1]\doprocessallactionsinset
+ \advance\processlevel \minusone
+ \fi
+ \expandactions}
+
+%D We can speed up these macros a bit when we use a dedicated
+%D commalist processor, one that avoids passing the (often)
+%D big action list.
+
+\beginTEX
+
+\def\processaction[#1]#2[%
+ {\let\donottest\dontprocesstest
+ \expandedaction\!!stringa{#1}%
+ \let\donottest\doprocesstest
+ \ifx\!!stringa\empty
+ \let\p!compareprocessaction\p!compareprocessactionA
+ \else
+ \let\p!compareprocessaction\p!compareprocessactionB
+ \fi
+ \def\p!doprocessaction##1%
+ {\p!compareprocessaction[##1][#1]}%
+ \processnextcommalist\relax\expandactions\p!doprocessaction[}
+
+\endTEX
+
+\beginETEX
+
+\def\processaction[#1]#2[%
+ {\expandedaction\!!stringa{#1}%
+ \ifx\!!stringa\empty
+ \let\p!compareprocessaction\p!compareprocessactionA
+ \else
+ \let\p!compareprocessaction\p!compareprocessactionB
+ \fi
+ \def\p!doprocessaction##1%
+ {\p!compareprocessaction[##1][#1]}%
+ \processnextcommalist\relax\expandactions\p!doprocessaction[}
+
+\endETEX
+
+\def\processfirstactionsinset[#1]%
+ {\expandedaction\!!stringa{#1}%
+ \ifx\!!stringa\empty
+ \expandafter\processaction
+ \else
+ \expandafter\processfirstactionsinsetindeed
+ \fi
+ [#1]}
+
+\def\processfirstactioninsetindeed[#1]#2[#3]%
+ {\def\p!doprocessaction##1%
+ {\def\p!dodoprocessaction####1%
+ {\p!compareprocessactionC[####1][##1]}%
+ \processcommalist[#3]\p!dodoprocessaction}%
+ \processcommalist[#1]\p!doprocessaction
+ \expandactions}
+
+\def\processallactionsinset[#1]%
+ {\expandedaction\!!stringa{#1}%
+ \ifx\!!stringa\empty
+ \expandafter\processaction
+ \else
+ \expandafter\processallactionsinsetindeed
+ \fi
+ [#1]}
+
+\def\processallactionsinsetindeed[#1]#2[#3]%
+ {\advance\processlevel \plusone
+ \expandafter\def\csname\s!do\the\processlevel\endcsname##1%
+ {\def\p!dodoprocessaction####1%
+ {\p!compareprocessactionD[####1][##1]}%
+ \processcommalist[#3]\p!dodoprocessaction}%
+ \processcommalist[#1]\doprocessallactionsinset
+ \advance\processlevel \minusone
+ \expandactions}
+
+\def\processnextcommalist#1#2#3[#4#5]%
+ {#1%
+ \let\nexttoken#4%
+ \global\advance\commalevel \plusone
+ \long\expandafter\def\csname\s!next\the\commalevel\endcsname##1,%
+ {#3{##1}\doprocesscommaitem}%
+ \dodoprocesscommaitem#4#5,]\relax
+ \global\advance\commalevel \minusone
+ #2}
+
+%D I do have an even faster version (saving 3 sec on a 13
+%D sec run for 50K invocations, but normally we don't have
+%D that many calls and that alternative uses more macros and is
+%D even less readable. What we did add, was \type {\@EA}, so
+%D that we can pass a command.
+
+%D \macros
+%D {unexpandedprocessaction,
+%D unexpandedprocessfirstactioninset,
+%D unexpandedprocessallactionsinset}
+%D
+%D Now what are those expansion commands doing there. Well,
+%D sometimes we want to compare actions that may consist off
+%D commands (i.e. are no constants). In such occasions we can
+%D use the a bit slower alternatives:
+
+\def\unexpandedprocessfirstactioninset{\dontexpandactions\processfirstactioninset}
+\def\unexpandedprocessaction {\dontexpandactions\processaction}
+\def\unexpandedprocessallactionsinset {\dontexpandactions\processallactionsinset}
+
+%D By default we expand actions:
+
+\def\expandactions{\let\expandedaction\edef} \expandactions
+
+%D But when needed we convert the strings to meaningful
+%D sequences of characters.
+
+\def\unexpandedaction#1>{}
+
+\def\noexpandedaction#1#2%
+ {\def\@@convertedargument{#2}%
+ \@EA\edef\@EA#1\@EA{\@EA\unexpandedaction\meaning\@@convertedargument}}
+
+\def\dontexpandactions%
+ {\let\expandedaction\noexpandedaction}
+
+%D \macros
+%D {getfirstcharacter, firstcharacter, remainingcharacters, doiffirstcharacter}
+%D
+%D Sometimes the action to be undertaken depends on the
+%D next character. This macro get this character and puts it in
+%D \type{\firstcharacter}.
+%D
+%D \starttyping
+%D \getfirstcharacter {string}
+%D \stoptyping
+%D
+%D A two step expansion is used to prevent problems with
+%D complicated arguments, for instance arguments that
+%D consist of two or more expandable tokens.
+
+\def\dogetfirstcharacter#1#2\relax
+ {\def\firstcharacter{#1}%
+ \def\remainingcharacters{#2}}
+
+\def\getfirstcharacter#1%
+ {\edef\!!stringa{#1}%
+ \expandafter\dogetfirstcharacter\!!stringa\relax}
+
+\def\doiffirstcharelse#1#2% char string
+% kort (maar onleesbaar)
+% {\expanded{\dogetfirstcharacter#2}\\\doifelse{#1}\firstcharacter}
+% korter (en begrijpelijk))
+ {\getfirstcharacter{#2}\doifelse{#1}\firstcharacter}
+% snel (maar zelden gebruikt, dus niet zo belangrijk)
+% {\getfirstcharacter{#2}%
+% \edef\!!stringa{#1}%
+% \ifx\!!stringa\firstcharacter
+% \expandafter\firstoftwoarguments
+% \else
+% \expandafter\secondoftwoarguments
+% \fi}
+
+%D \macros
+%D {doifinstringelse, doifincsnameelse}
+%D
+%D We can check for the presence of a substring in a given
+%D sequence of characters.
+%D
+%D \starttyping
+%D \doifinsetelse {substring} {string} {then ...} {else ...}
+%D \stoptyping
+%D
+%D An application of this command can be found further on.
+%D Like before, we first show some alternatives, like the one
+%D we started with:
+%D
+%D \starttyping
+%D \long\def\p!doifinstringelse#1#2#3#4%
+%D {\def\pp!doifinstringelse##1#1##2##3\war%
+%D {\if##2@%
+%D #4%
+%D \else
+%D #3%
+%D \fi}%
+%D \pp!doifinstringelse#2#1@@\war}
+%D
+%D \def\doifinstringelse%
+%D {\ExpandBothAfter\p!doifinstringelse}
+%D \stoptyping
+%D
+%D After this we came to:
+%D
+%D \starttyping
+%D \def\p!doifinstringelse#1#2%
+%D {\def\pp!doifinstringelse##1#1##2##3\war%
+%D {\if##2@}%
+%D \pp!doifinstringelse#2#1@@\war}
+%D
+%D \def\doifinstringelse#1#2#3#4%
+%D {\ExpandBothAfter\p!doifinstringelse{#1}{#2}%
+%D #4%
+%D \else
+%D #3%
+%D \fi}
+%D \stoptyping
+%D
+%D Sometimes the second argument is passed as a macro. By
+%D postponing the expansion of this macro, we gain quite some
+%D run time, simply because the less tokens we pass, the faster
+%D \TEX\ runs. So finally the definition became:
+
+% \long\def\rawdoifinstringelse#1#2% ##2 can be {abc}
+% {\long\def\pp!doifinstringelse##1#1##2##3\war{\if##2@}%
+% \pp!doifinstringelse#2#1@@\war
+% \expandafter\secondoftwoarguments
+% \else
+% \expandafter\firstoftwoarguments
+% \fi}
+
+\long\def\doifinstringelse#1%
+ {\edef\@@@instring{#1}% expand #1 here
+ \ifx\@@@instring\empty
+ \@EA\thirdofthreearguments
+ \else
+ \@EA\dodoifinstringelse
+ \fi}
+
+\long\def\dodoifinstringelse#1%
+ {\p!doifinstringelse\@@@instring{#1}%
+ \@EA\firstoftwoarguments
+ \else
+ \@EA\secondoftwoarguments
+ \fi}
+
+\long\def\doifinstring#1%%
+ {\edef\@@@instring{#1}% expand #1 here
+ \ifx\@@@instring\empty
+ \@EA\gobbletwoarguments
+ \else
+ \@EA\dodoifinstring
+ \fi}
+
+\long\def\dodoifinstring#1%
+ {\p!doifinstringelse\@@@instring{#1}%
+ \@EA\firstofoneargument
+ \else
+ \@EA\gobbleoneargument
+ \fi}
+
+\long\def\doifnotinstring#1%%
+ {\edef\@@@instring{#1}% expand #1 here
+ \ifx\@@@instring\empty
+ \@EA\gobbletwoarguments
+ \else
+ \@EA\dodoifnotinstring
+ \fi}
+
+\long\def\dodoifnotinstring#1%
+ {\p!doifinstringelse\@@@instring{#1}%
+ \@EA\gobbleoneargument
+ \else
+ \@EA\firstofoneargument
+ \fi}
+
+%D \starttyping
+%D \beginTEX
+%D
+%D \long\def\p!doifinstringelse#1#2%
+%D {\long\def\pp!doifinstringelse##1#1##2##3\war%
+%D {\csname if\if##2@fals\else tru\fi e\endcsname}%
+%D \expanded{\pp!doifinstringelse#2#1@@\noexpand\war}} % expand #2 here
+%D
+%D \endTEX
+%D
+%D \beginETEX \unless
+%D
+%D \long\def\p!doifinstringelse#1#2%
+%D {\long\def\pp!doifinstringelse##1#1##2##3\war%
+%D {\unless\if##2@}%
+%D \expanded{\pp!doifinstringelse#2#1@@\noexpand\war}} % expand #2 here
+%D
+%D \endETEX
+%D \stoptyping
+%D
+%D And then \unknown\ after a couple of years, we ran into a
+%D situation where \type {##2} was something \type {{bla}}. So
+%D finally we need to use an auxiliary macro, otherwise we get
+%D funny strings in the output.
+
+% \long\def\p!doifinstringelse#1#2% ##2 can be {abc}
+% {\long\@EA\def\@EA\pp!doifinstringelse\@EA##\@EA1#1##2##3\war % expand #1 here
+% {\ppp!doifinstringelse##2\war}%
+% \expanded{\pp!doifinstringelse#2#1@@\noexpand\war}} % expand #2 here
+%
+% \beginTEX
+%
+% \def\ppp!doifinstringelse#1#2\war%
+% {\csname if\ifx#1@fals\else tru\fi e\endcsname}%
+%
+% \endTEX
+%
+% \beginETEX \unless
+%
+% \def\ppp!doifinstringelse#1#2\war%
+% {\unless\ifx#1@}
+%
+% \endETEX
+
+\beginETEX
+
+\long\def\p!doifinstringelse#1#2% ##2 can be {abc}
+ {\long\@EA\def\@EA\pp!doifinstringelse\@EA##\@EA1#1##2##3\war % expand #1 here
+ {\unless\if##2@}%
+% \expanded{\pp!doifinstringelse#2#1@@\noexpand\war}} % expand #2 here
+ \expanded{\pp!doifinstringelse#2#1}@@\war} % expand #2 here
+
+\endETEX
+
+\beginTEX
+
+\long\def\p!doifinstringelse#1#2% ##2 can be {abc}
+ {\long\@EA\def\@EA\pp!doifinstringelse\@EA##\@EA1#1##2##3\war % expand #1 here
+ {\csname if\if##2@fals\else tru\fi e\endcsname}%
+ %\expanded{\pp!doifinstringelse#2#1@@\noexpand\war}} % expand #2 here
+ \expanded{\pp!doifinstringelse#2#1}@@\war} % expand #2 here
+
+\endTEX
+
+%D The next alternative proved to be upto twice as fast on
+%D tasks like checking reserved words in pretty verbatim
+%D typesetting! This is mainly due to the fact that passing
+%D (expanded) strings is much slower that passing a macro.
+%D
+%D \starttyping
+%D \doifincsnameelse {substring} {\string} {then ...} {else ...}
+%D \stoptyping
+%D
+%D Where \type{\doifinstringelse} does as much expansion as
+%D possible, the latter alternative does minimal (one level)
+%D expansion.
+
+\beginTEX
+
+\long\def\p!doifincsnameelse#1#2%
+ {\long\def\pp!doifincsnameelse##1#1##2##3\war
+ {\csname if\if##2@fals\else tru\fi e\endcsname}%
+ \@EA\pp!doifincsnameelse#2#1@@\war}
+
+\endTEX
+
+\beginETEX \unless
+
+\long\def\p!doifincsnameelse#1#2%
+ {\long\def\pp!doifincsnameelse##1#1##2##3\war
+ {\unless\if##2@}%
+ \@EA\pp!doifincsnameelse#2#1@@\war}
+
+\endETEX
+
+\long\def\doifincsnameelse#1#2% % #3#4%
+ {\edef\@@@instring{#1}%
+ \@EA\p!doifincsnameelse\@EA{\@@@instring}{#2}% % #3\else#4\fi}
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+%D \macros
+%D {doifnumberelse}
+%D
+%D The next macro executes a command depending of the outcome
+%D of a test on numerals. This is probably one of the fastest
+%D test possible, exept from a less robust 10||step
+%D \type{\if}||ladder or some tricky \type{\lcode} checking.
+%D
+%D \starttyping
+%D \doifnumberelse {string} {then ...} {else ...}
+%D \stoptyping
+%D
+%D The macro accepts \type{123}, \type{abc}, \type{{}},
+%D \type{\getal} and \type{\the\count...}. This macro is a
+%D rather dirty one.
+%D
+%D \starttyping
+%D \long\def\doifnumberelse#1#2#3%
+%D {\begingroup\donefalse
+%D \ifcase1#1\or\or\or\or\or\or\or\or\or\else\donetrue\fi
+%D \ifdone\endgroup#2\else\endgroup#3\fi}
+%D \stoptyping
+%D
+%D Or better:
+%D
+%D \starttyping
+%D \long\def\doifnumberelse#1%
+%D {\begingroup\donefalse
+%D \ifcase1#1\or\or\or\or\or\or\or\or\or\else\donetrue\fi
+%D \ifdone
+%D \endgroup\expandafter\firstoftwoarguments
+%D \else
+%D \endgroup\expandafter\secondoftwoarguments
+%D \fi}
+%D \stoptyping
+%D
+%D A previous implementation was:
+%D
+%D \starttyping
+%D \long\def\doifnumberelse#1#2#3%
+%D {\getfirstcharacter{#1}%
+%D \@EA\p!doifinstringelse\@EA{\firstcharacter}{1234567890}%
+%D #2%
+%D \else
+%D #3%
+%D \fi}
+%D \stoptyping
+%D
+%D And before we had \type{\p!doifinstringelse} available, we
+%D used:
+%D
+%D \starttyping
+%D \def\doifnumberelse#1%
+%D {\getfirstcharacter{#1}%
+%D \rawdoifinsetelse{\firstcharacter}{1,2,3,4,5,6,7,8,9,0}}
+%D \stoptyping
+%D
+%D The implementation using \type {\ifcase} is much faster, but
+%D the next one is not, not even when testing milion calls.
+%D
+%D \starttyping
+%D \newif\ifitsanumber
+%D
+%D \long\def\isitanumber#1%
+%D {\itsanumberfalse
+%D \ifcase1#1\or\or\or\or\or\or\or\or\or\else\itsanumbertrue\fi}
+%D
+%D \long\def\doifnumberelse#1#2#3%
+%D {\isitanumber{#1}\ifitsanumber#2\else#3\fi}
+%D \stoptyping
+%D
+%D After a while the next evolved and this one is the one we
+%D will use. This one is some 5\% faster than the group/done
+%D one (partly because it does not have to pass arguments).
+%D Even more important is that this alternative is fully
+%D expandable!
+
+\long\def\doifnumberelse#1% does not accept counters
+ {\ifcase0\ifcase1#1\or\or\or\or\or\or\or\or\or\else1\fi\space
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+%D \macros
+%D {makerawcommalist,
+%D rawdoinsetelse,
+%D rawprocesscommalist,
+%D rawprocessaction}
+%D
+%D Some of the commands mentioned earlier are effective but
+%D slow. When one is desperately in need of faster alternatives
+%D and when the conditions are predictable safe, the \type{\raw}
+%D alternatives come into focus. A major drawback is that
+%D they do not take \type{\c!constants} into account, simply
+%D because no expansion is done. This is no problem with
+%D \type{\rawprocesscommalist}, because this macro does not
+%D compare anything. Expandable macros are permitted as search
+%D string.
+%D
+%D \starttyping
+%D \makerawcommalist[string,string,...]\stringlist
+%D \rawdoifinsetelse{string}{string,...}{...}{...}
+%D \rawprocesscommalist[string,string,...]\commando
+%D \rawprocessaction[x][a=>\a,b=>\b,c=>\c]
+%D \stoptyping
+%D
+%D Spaces embedded in the list, for instance after commas,
+%D spoil the search process. The gain in speed depends on the
+%D length of the argument (the longer the argument, the less
+%D we gain).
+%D
+%D The slow alternative looks like:
+%D
+%D \starttyping
+%D \def\makerawcommalist[#1]#2%
+%D {\def\appendtocommalist##1%
+%D {\doifelse{#2}{}
+%D {\edef#2{##1}}
+%D {\edef#2{#2,##1}}}%
+%D \def#2{}%
+%D \processcommalist[#1]\appendtocommalist}
+%D \stoptyping
+%D
+%D But we prefer:
+
+% \appendtocommalist is defined in syst-ext
+
+\def\makerawcommalist[#1]#2% use \processnext ... here
+ {\def\domakerawcommalist##1% we don't expand ##1
+ {\ifx#2\empty
+ \def#2{##1}%
+ \else
+ \@EA\def\@EA#2\@EA{#2,##1}%
+ \fi}%
+ \let#2\empty
+ \processcommalist[#1]\domakerawcommalist}
+
+\def\rawprocesscommaitem#1,#2% #2 eats up preceding space
+ {\if]#1\else
+ \csname\s!next\the\commalevel\endcsname{#1}%
+ \expandafter\rawprocesscommaitem
+ \fi#2}
+
+\def\rawprocesscommalist[#1]#2% accepteert ook [\cs]
+ {\global\advance\commalevel \plusone
+ \expandafter\let\csname\s!next\the\commalevel\endcsname#2%
+ \expandafter\rawprocesscommaitem#1,],% \relax
+ \global\advance\commalevel \minusone }
+
+\def\rawprocesscommacommand[#1]% not really needed
+ {\expanded{\rawprocesscommalist[#1]}}
+
+% \def\rawdoifinsetelse#1#2{\doifinstringelse{,#1,}{,#2,}}
+% \def\rawdoifinset #1#2{\doifinstring {,#1,}{,#2,}}
+
+\def\@@rawempty{,,}
+
+\long\def\rawdoifinsetelse#1%
+ {\edef\@@@instring{,#1,}% expand #1 here
+ \ifx\@@@instring\@@rawempty
+ \@EA\thirdofthreearguments
+ \else
+ \@EA\rawdodoifinsetelse
+ \fi}
+
+\long\def\rawdodoifinsetelse#1%
+ {\p!doifinstringelse\@@@instring{,#1,}%
+ \@EA\firstoftwoarguments
+ \else
+ \@EA\secondoftwoarguments
+ \fi}
+
+\long\def\rawdoifinset#1%
+ {\edef\@@@instring{,#1,}% expand #1 here
+ \ifx\@@@instring\@@rawempty
+ \@EA\gobbletwoarguments
+ \else
+ \@EA\rawdodoifinset
+ \fi}
+
+\long\def\rawdodoifinset#1%%
+ {\p!doifinstringelse\@@@instring{,#1,}%
+ \@EA\firstofoneargument
+ \else
+ \@EA\gobbleoneargument
+ \fi}
+
+%D Some more raw material:
+
+\def\p!rawprocessaction[#1][#2]%
+ {\def\pp!rawprocessaction##1,#1=>##2,##3\war%
+ {\if##3@\else
+ \def\!!processaction{##2}%
+ \fi}%
+ \pp!rawprocessaction,#2,#1=>,@\war}
+
+\def\rawprocessaction[#1]#2[#3]%
+ {\edef\!!stringa{#1}%
+ \edef\!!stringb{undefined}% better \!!undefined
+ \let\!!processaction\!!stringb
+ \ifx\!!stringa\empty
+ \@EA\p!rawprocessaction\@EA[\s!default][#3]%
+ \else
+ \expandafter\p!rawprocessaction\expandafter[\!!stringa][#3]%
+ \ifx\!!processaction\!!stringb
+ \@EA\p!rawprocessaction\@EA[\s!unknown][#3]%
+ \fi
+ \fi
+ \ifx\!!processaction\!!stringb
+ \else
+ \!!processaction
+ \fi}
+
+% not needed
+%
+% \def\rawprocessallactionsinset[#1]#2[#3]%
+% {\def\docommand##1%
+% {\rawprocessaction[##1][#3]}%
+% \processcommalist[#1]\docommand}
+
+%D When we process the list \type{a,b,c,d,e}, the raw routine
+%D takes over 30\% less time, when we feed $20+$ character
+%D strings we gain about 20\%. Alternatives which use
+%D \type{\futurelet} perform worse. Part of the speedup is
+%D due to the \type{\let} and \type{\expandafter} in the test.
+
+% %D \macros
+% %D {processunexpandedcommalist}
+% %D
+% %D When processing commalists, the arguments are expanded. The
+% %D main reason for doing so lays in the fact that these
+% %D macros are used for interfacing. The next alternative can be used
+% %D for
+% %D
+% %D \starttyping
+% %D \processunexpandedcommalist
+% %D [\alfa\beta,\gamma,\delta\epsilon]
+% %D \handleitem
+% %D \stoptyping
+% %D
+% %D This time nesting is not supported.
+%
+% %\def\processunexpandedcommaitem#1,%
+% % {\if]\noexpand#1%
+% % \let\nextcommaitem\relax
+% % \else
+% % \handleunexpandedcommaitem{#1}%
+% % \let\nextcommaitem\processunexpandedcommaitem
+% \fi
+% \nextcommaitem}
+%
+% faster:
+%
+% \def\processunexpandedcommaitem#1,%
+% {\if]\noexpand#1\else
+% \handleunexpandedcommaitem{#1}%
+% \expandafter\processunexpandedcommaitem
+% \fi}
+%
+% \def\processunexpandedcommalist[#1]#2%
+% {\def\handleunexpandedcommaitem{#2}%
+% \processunexpandedcommaitem#1,],}% \relax}
+%
+% %D Or faster:
+%
+% \def\processunexpandedcommaitem#1,%
+% {\if]\noexpand#1\else
+% \handleunexpandedcommaitem{#1}%
+% \expandafter\processunexpandedcommaitem
+% \fi}
+
+%D \macros
+%D {dosetvalue,dosetevalue,dosetgvalue,docopyvalue,doresetvalue,
+%D dogetvalue}
+%D
+%D When we are going to do assignments, we have to take
+%D multi||linguality into account. For the moment we keep
+%D things simple and single||lingual.
+%D
+%D \starttyping
+%D \dosetvalue {label} {variable} {value}
+%D \dosetevalue {label} {variable} {value}
+%D \dosetgvalue {label} {variable} {value}
+%D \docopyvalue {to label} {from label} {variable}
+%D \doresetvalue {label} {variable}
+%D \stoptyping
+%D
+%D These macros are in fact auxiliary ones and are not meant
+%D for use outside the assignment macros.
+
+\def\dosetvalue#1#2% #3
+ {\@EA\def\csname#1#2\endcsname} % {#3}}
+
+\def\dosetevalue#1#2% #3
+ {\@EA\edef\csname#1#2\endcsname} % {#3}}
+
+\def\dosetgvalue#1#2% #3
+ {\@EA\gdef\csname#1#2\endcsname} % {#3}}
+
+\def\doresetvalue#1#2%
+ {\@EA\let\csname#1#2\endcsname\empty}
+
+\def\doignorevalue#1#2#3%
+ {\@EA\let\csname#1#2\endcsname\empty}
+
+\def\docopyvalue#1#2#3%
+ {\@EA\def\csname#1#3\endcsname{\csname#2#3\endcsname}}
+
+%D \macros
+%D {doassign,undoassign,doassignempty}
+%D
+%D Assignments are the backbone of \CONTEXT. Abhorred by the
+%D concept of style file hacking, we took a considerable effort
+%D in building a parameterized system. Unfortunately there is a
+%D price to pay in terms of speed. Compared to other packages
+%D and taking the functionality of \CONTEXT\ into account, the
+%D total size of the format file is still very acceptable. Now
+%D how are these assignments done.
+%D
+%D Assignments can be realized with:
+%D
+%D \starttyping
+%D \doassign[label][variable=value]
+%D \undoassign[label][variable=value]
+%D \stoptyping
+%D
+%D and:
+%D
+%D \starttyping
+%D \doassignempty[label][variable=value]
+%D \stoptyping
+%D
+%D Assignments like \type{\doassign} are compatible with:
+%D
+%D \starttyping
+%D \def\labelvariable{value}
+%D \stoptyping
+%D
+%D We do check for the presence of an \type{=} and loudly
+%D complain of it's missed. We will redefine this macro later
+%D on, when a more advanced message mechanism is implemented.
+
+\newif\iferrorisfatal
+
+\def\waitonfatalerror
+ {\iferrorisfatal\wait\fi}
+
+\def\showassignerror#1#2%
+ {\writestatus{setup}{missing or ungrouped '=' after '#1' in line #2}%
+ \waitonfatalerror}
+
+%\def\p!doassign#1[#2][#3=#4=#5]%
+% {\let\donottest\dontprocesstest
+% \edef\!!stringa{#5}%
+% \let\!!stringb\relax
+% \let\donottest\doprocesstest
+% \ifx\!!stringa\!!stringb
+% \showassignerror{#3}%
+% \else
+% #1{#2}{#3}{#4}%
+% \fi}
+
+\def\p!doassign#1[#2][#3=#4=#5]%
+ {\ifx\empty#3\else % and definitely not \ifx#3\empty
+ \ifx\relax#5%
+ \showassignerror{#3}{\the\inputlineno\space(#2)}%
+ \else
+ #1{#2}{#3}{#4}%
+ \fi
+ \fi}
+
+\def\doassign [#1][#2]{\p!doassign\dosetvalue [#1][#2==\relax]}
+\def\doeassign [#1][#2]{\p!doassign\dosetevalue [#1][#2==\relax]}
+\def\undoassign[#1][#2]{\p!doassign\doresetvalue[#1][#2==\relax]}
+
+\def\doassignempty[#1][#2=#3]%
+ {\ifundefined{#1#2}\dosetvalue{#1}{#2}{#3}\fi}
+
+%D \macros
+%D {processassignmentlist,processassignmentcommand,
+%D startprocessassignmentlist,startprocessassignmentcommand}
+%D
+%D For Wolfgang:
+%D
+%D \starttyping
+%D \def\showpair#1#2{key:#1, value:#2\par}
+%D \processassignmentlist[a=1,b=2]\showpair
+%D \stoptyping
+
+\def\processassignmentlist[#1]#2% #2 == \command{key}{value]
+ {\def\doprocessassignmententry##1{#2}% {##2}{##3} % namespace is ignored
+ \dogetparameters\doprocessassignmententry[][#1]}
+
+\def\processassignmentcommand[#1]%
+ {\normalexpanded{\noexpand\processassignmentlist[#1]}}
+
+\long\def\startprocessassignmentlist[#1]#2\stopprocessassignmentlist
+ {\long\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}%
+ \processassignmentlist[#1]\currentassignmentlistcommand}
+
+\long\def\startprocessassignmentcommand[#1]#2\stopprocessassignmentcommand
+ {\long\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}%
+ \normalexpanded{\noexpand\processassignmentlist[#1]}\currentassignmentlistcommand}
+
+%D \macros
+%D {getparameters,geteparameters,getgparameters,
+%D forgetparameters}
+%D
+%D Using the assignment commands directly is not our
+%D ideal of user friendly interfacing, so we take some further
+%D steps.
+%D
+%D \starttyping
+%D \getparameters [label] [...=...,...=...]
+% %D \forgetparameters [label] [...=...,...=...]
+%D \stoptyping
+%D
+%D Again, the label identifies the category a variable
+%D belongs to. The second argument can be a comma separated
+%D list of assignments.
+%D
+%D \starttyping
+%D \getparameters
+%D [demo]
+%D [alfa=1,
+%D beta=2]
+%D \stoptyping
+%D
+%D is equivalent to
+%D
+%D \starttyping
+%D \def\demoalfa{1}
+%D \def\demobeta{2}
+%D \stoptyping
+%D
+%D
+%D In the pre||multi||lingual stadium \CONTEXT\ took the next
+%D approach. With
+%D
+%D \starttyping
+%D \def\??demo {@@demo}
+%D \def\!!alfa {alfa}
+%D \def\!!beta {beta}
+%D \stoptyping
+%D
+%D calling
+%D
+%D \starttyping
+%D \getparameters
+%D [\??demo]
+%D [\!!alfa=1,
+%D \!!beta=2]
+%D \stoptyping
+%D
+%D lead to:
+%D
+%D \starttyping
+%D \def\@@demoalfa{1}
+%D \def\@@demobeta{2}
+%D \stoptyping
+%D
+%D Because we want to be able to distinguish the \type{!!}
+%D pre||tagged user supplied variables from internal
+%D counterparts, we will introduce a slightly different tag in
+%D the multi||lingual modules. There we will use \type{c!} or
+%D \type{v!}, depending on the context.
+%D
+%D By calling \type{\p!doassign} directly, we save ourselves
+%D some argument passing and gain some speed. Whatever
+%D optimizations we do, this command will always be one of the
+%D bigger bottlenecks.
+%D
+%D The alternative \type{\geteparameters} --- it's funny to
+%D see that this alternative saw the light so lately --- can be
+%D used to do expanded assigments.
+
+\def\dogetparameters#1[#2]#3[#4]%
+ {\def\p!dogetparameter##1%
+ {\p!doassign#1[#2][##1==\relax]}%
+ \processcommalist[#4]\p!dogetparameter}
+
+\def\getparameters {\dogetparameters\dosetvalue}
+\def\geteparameters {\dogetparameters\dosetevalue}
+\def\getgparameters {\dogetparameters\dosetgvalue}
+\def\forgetparameters{\dogetparameters\doignorevalue}
+
+\let\getexpandedparameters=\geteparameters
+
+%D This one is slightly faster:
+
+\def\dogetparameters#1[#2]#3[#4%
+ {\if\noexpand#4]%
+ \expandafter\gobbleoneargument
+ \else
+ \def\p!dogetparameter##1{\p!doassign#1[#2][##1==\relax]}%
+ \expandafter\xdogetparameters
+ \fi#4}
+
+\def\xdogetparameters#1]%
+ {\processcommalist[#1]\p!dogetparameter}
+
+%D The next alternative is much faster but also uglier. Because
+%D in \XML\ processing we will probably set much more parameters
+%D than normally we need this faster one.
+
+\def\dogetparameters#1[#2]#3[#4%
+ {\if\noexpand#4]%
+ \expandafter\gobbleoneargument
+ \else
+ \def\p!dogetparameter{\p!doassign#1#2}%
+ \expandafter\xdogetparameters
+ \fi#4}
+
+\def\xdogetparameters#1]%
+ {\xprocesscommaitem#1,],\@relax@}
+
+% \long\def\xprocesscommaitem#1,#2% #2 takes space before ,
+% {\if]#1%
+% \expandafter\gobbleoneargument
+% \else
+% \p!dogetparameter\@relax@#1==\@relax@
+% \expandafter\xprocesscommaitem
+% \fi#2}
+
+\long\def\xprocesscommaitem#1,#2% #2 takes space before ,
+ {\if,#1,% dirty trick for testing #1=empty
+ \@EA\xprocesscommaitem
+ \else\if]#1%
+ \@EAEAEA\gobbleoneargument
+ \else
+ \p!dogetparameter\@relax@#1==\empty\@relax@
+ \@EAEAEA\xprocesscommaitem
+ \fi\fi#2}
+
+%D Here we use a slightly different assignment macro:
+
+% \def\p!doassign#1#2\@relax@#3=#4=#5\@relax@
+% {\ifx\@relax@#5\@EA\xshowassignerror\else\@EA#1\fi{#2}{#3}{#4}}
+
+% \def\p!doassign#1#2\@relax@#3=#4=#5\@relax@
+% {\ifx#5\empty\@EA\xshowassignerror\else\@EA#1\fi{#2}{#3}{#4}}
+
+\def\p!doassign#1#2\@relax@#3=#4=#5#6\@relax@
+ {\ifx#5\empty
+ \@EA\xshowassignerror
+ \else\ifx#5=%
+ \@EAEAEA#1%
+ \else
+ \@EAEAEA\xshowassignerror
+ \fi\fi
+ {#2}{#3}{#4}}
+
+\def\xshowassignerror#1#2#3%
+ {\showassignerror{#2}{\the\inputlineno\space(#1)}}
+
+%D Now we also have to change the other macros that depend
+%D on this low level one.
+
+% \def\doassign [#1][#2]{\p!doassign\dosetvalue #1\@relax@#2==\@relax@}
+% \def\doeassign [#1][#2]{\p!doassign\dosetevalue #1\@relax@#2==\@relax@}
+% \def\undoassign[#1][#2]{\p!doassign\doresetvalue#1\@relax@#2==\@relax@}
+
+\def\doassign [#1][#2]{\p!doassign\dosetvalue #1\@relax@#2==\empty\@relax@}
+\def\doeassign [#1][#2]{\p!doassign\dosetevalue #1\@relax@#2==\empty\@relax@}
+\def\undoassign[#1][#2]{\p!doassign\doresetvalue#1\@relax@#2==\empty\@relax@}
+
+%D When someone asked on the mailing list if it's possible to
+%D use the current value of a parameter, Taco posted a small module. His
+%D method had the disadvantage of making all assignments expanded and thereby
+%D fragile. The following alternative uses a prefix.
+
+%D \macros{currentvalue}
+%D
+%D Just in case a \type{\getparameter} argument itself ends up
+%D inside a \type{\write} or other expandable location, our
+%D new macro needs a default value.
+
+\let\currentvalue\empty
+
+% \def\p!n!doassign#1#2\@relax@#3=#4=#5#6\@relax@% normal
+% {\ifx#5\empty
+% \@EA\xshowassignerror
+% \else\ifx#5=%
+% \@EAEAEA#1%
+% \else
+% \@EAEAEA\xshowassignerror
+% \fi\fi
+% {#2}{#3}{#4}}
+
+\def\p!n!doassign#1#2\@relax@#3=#4=#5#6\@relax@
+ {\ifx\empty#3\empty
+ \@EA\xshowassignerror
+ \else\ifx#5\empty
+ \@EAEAEA\xshowassignerror
+ \else
+ \@EAEAEA#1%
+ \fi\fi
+ {#2}{#3}{#4}}
+
+\beginTEX
+
+% \def\p!e!doassign#1#2\@relax@#3=#4=#5#6\@relax@
+% {\ifx#5\empty
+% \@EA\xshowassignerror
+% \else\ifx#5=%
+% \@EA\ifx\csname#2#3\endcsname\relax
+% \let\currentvalue\empty
+% \else
+% \@EA\let\@EA\currentvalue\csname#2#3\endcsname
+% \fi
+% \@EAEAEA#1%
+% \else
+% \@EAEAEA\xshowassignerror
+% \fi\fi
+% {#2}{#3}{#4}}
+
+\def\p!e!doassign#1#2\@relax@#3=#4=#5#6\@relax@
+ {\ifx\empty#3\empty
+ \@EA\xshowassignerror
+ \else\ifx#5\empty
+ \@EAEAEA\xshowassignerror
+ \else
+ \@EA\ifx\csname#2#3\endcsname\relax
+ \let\currentvalue\empty
+ \else
+ \@EA\let\@EA\currentvalue\csname#2#3\endcsname
+ \fi
+ \@EAEAEA#1%
+ \fi\fi
+ {#2}{#3}{#4}}
+
+\endTEX
+
+\beginETEX
+
+% \def\p!e!doassign#1#2\@relax@#3=#4=#5#6\@relax@
+% {\ifx#5\empty
+% \@EA\xshowassignerror
+% \else\ifx#5=%
+% \ifcsname#2#3\endcsname
+% \@EA\let\@EA\currentvalue\csname#2#3\endcsname
+% \else
+% \let\currentvalue\empty
+% \fi
+% \@EAEAEA#1%
+% \else
+% \@EAEAEA\xshowassignerror
+% \fi\fi
+% {#2}{#3}{#4}}
+
+\def\p!e!doassign#1#2\@relax@#3=#4=#5#6\@relax@
+ {\ifx\empty#3\empty
+ \@EA\xshowassignerror
+ \else\ifx#5\empty
+ \@EAEAEA\xshowassignerror
+ \else
+ \ifcsname#2#3\endcsname
+ \@EA\let\@EA\currentvalue\csname#2#3\endcsname
+ \else
+ \let\currentvalue\empty
+ \fi
+ \@EAEAEA#1%
+ \fi\fi
+ {#2}{#3}{#4}}
+
+\endETEX
+
+%D We default to:
+
+\let\p!doassign\p!n!doassign
+
+%D And set:
+
+\let\currentvalue\empty
+
+%D \macros {expandparameters}
+%D
+%D Example usage:
+%D
+%D \startbuffer
+%D \getparameters[taco][name=taco]
+%D \convertcommand\taconame\to\ascii \ascii
+%D \expandparameters \getparameters[taco][name=\currentvalue\space hoekwater]
+%D \convertcommand\taconame\to\ascii \ascii
+%D \getparameters[taco][name=\currentvalue\space hoekwater]
+%D \convertcommand\taconame\to\ascii \ascii
+%D \stopbuffer
+%D
+%D \typebuffer
+%D \startlines
+%D \getbuffer
+%D \stoplines
+
+%D Here we hook in the code (beware, this is the optimized get **):
+
+\def\xdoget@n@parameters#1]%
+ {\xprocesscommaitem#1,],\@relax@}
+
+\def\xdoget@e@parameters#1]%
+ {\let\dosetnvalue\dosetvalue
+ \let\dosetvalue\dosetevalue
+ \let\p!doassign\p!e!doassign
+ \xprocesscommaitem#1,],\@relax@
+ \let\p!doassign\p!n!doassign
+ \let\dosetvalue\dosetnvalue
+ \let\xdogetparameters\xdoget@n@parameters
+ \let\currentvalue\empty}
+
+\let\xdogetparameters\xdoget@n@parameters % **
+
+\def\expandparameters{\let\xdogetparameters\xdoget@e@parameters}
+
+%D \macros
+%D {getemptyparameters}
+%D
+%D Sometimes we explicitly want variables to default to an
+%D empty string, so we welcome:
+%D
+%D \starttyping
+%D \getemptyparameters [label] [...=...,...=...]
+%D \stoptyping
+
+\def\getemptyparameters[#1]#2[#3]%
+ {\def\p!dogetemptyparameter##1%
+ {\doassignempty[#1][##1]}%
+ \processcommalist[#3]\p!dogetemptyparameter}
+
+%D \macros
+%D {copyparameters}
+%D
+%D Some \CONTEXT\ commands take their default setups from
+%D others. All commands that are able to provide backgounds
+%D or rules around some content, for instance default to the
+%D standard command for ruled boxes. Is situations like this
+%D we can use:
+%D
+%D \starttyping
+%D \copyparameters [to-label] [from-label] [name1,name2,...]
+%D \stoptyping
+%D
+%D For instance
+%D
+%D \starttyping
+%D \copyparameters
+%D [internal][external]
+%D [alfa,beta]
+%D \stoptyping
+%D
+%D Leads to:
+%D
+%D \starttyping
+%D \def\internalalfa {\externalalfa}
+%D \def\internalbeta {\externalbeta}
+%D \stoptyping
+%D
+%D By using \type{\docopyvalue} we've prepared this command
+%D for use in a multi||lingual environment.
+
+\def\copyparameters[#1]#2[#3]#4[#5]%
+ {\doifnot{#1}{#3}
+ {\def\docopyparameter{\docopyvalue{#1}{#3}}%
+ %\def\docopyparameter##1{\docopyvalue{#1}{#3}{##1}}%
+ \processcommalist[#5]\docopyparameter}}
+
+%D \macros
+%D {ifparameters,checkparameters}
+%D
+%D A slightly different one is \type{\checkparameters}, which
+%D also checks on the presence of a~\type{=}.
+%D
+%D The boolean \type{\ifparameters} can be used afterwards.
+%D Combining both in one \type{\if}||macro would lead to
+%D problems with nested \type{\if}'s.
+%D
+%D \starttyping
+%D \checkparameters[argument]
+%D \stoptyping
+
+\newif\ifparameters
+
+\def\p!checkparameters#1=#2#3\war%
+ {\if#2@\parametersfalse\else\parameterstrue\fi}
+
+\def\checkparameters[#1]%
+ {\p!checkparameters#1=@@\war}
+
+%D \macros
+%D {getfromcommalist,getfromcommacommand,
+%D commalistelement,
+%D getcommalistsize,getcommacommandsize}
+%D
+%D It's possible to get an element from a commalist or a
+%D command representing a commalist.
+%D
+%D \starttyping
+%D \getfromcommalist [string] [n]
+%D \getfromcommacommand [string,\strings,string,...] [n]
+%D \stoptyping
+%D
+%D The difference betwee the two of them is the same as the
+%D difference between \type{\processcomma...}. The found string
+%D is stored in \type{\commalistelement}.
+%D
+%D We can calculate the size of a comma separated list by
+%D using:
+%D
+%D \starttyping
+%D \getcommalistsize [string,string,...]
+%D \getcommacommandsize [string,\strings,string,...]
+%D \stoptyping
+%D
+%D Afterwards, the length is available in the macro
+%D \type{\commalistsize} (not a \COUNTER).
+
+\newcount\commalistcounter
+
+\def\commalistsize{0}
+
+\def\p!dogetcommalistsize#1%
+ {\advance\commalistcounter\plusone}
+
+\def\getcommalistsize#1]% don't loose [{#1}]
+ {\commalistcounter\zerocount
+ \processcommalist#1]\p!dogetcommalistsize % was [{#1}]
+ \edef\commalistsize{\the\commalistcounter}}
+
+\def\getcommacommandsize[#1]%
+ {\edef\commacommand{#1}%
+ \scratchtoks\expandafter{\expandafter[\commacommand]}%
+ \expandafter\getcommalistsize\the\scratchtoks }
+
+% to be tested first
+%
+% \def\getcommacommandsize[#1]%
+% {\expanded{\getcommalistsize[#1]}}
+
+% \def\p!dogetfromcommalist#1%
+% {\advance\commalistcounter \minusone
+% \ifcase\commalistcounter
+% \def\commalistelement{#1}%
+% \begingroup\def\doprocesscommaitem##1]{\endgroup}%
+% \fi}
+
+\def\p!dogetfromcommalist#1%
+ {\advance\commalistcounter \minusone
+ \ifcase\commalistcounter
+ \def\commalistelement{#1}%
+ \expandafter\quitcommalist
+ \fi}
+
+\def\getfromcommalist[#1]#2[#3]%
+ {\let\commalistelement\empty
+ \commalistcounter#3\relax
+ \processcommalist[#1]\p!dogetfromcommalist}
+
+% \def\getfromcommacommand[#1]% why so complicated, still needed?
+% {\edef\commacommand{#1}%
+% \toks0=\expandafter{\expandafter[\commacommand]}%
+% \expandafter\getfromcommalist\the\toks0 }
+
+\def\getfromcommacommand[#1]%
+ {\expanded{\getfromcommalist[#1]}}
+
+%D Because 0, 1 and~2 are often asked for, we optimize this
+%D macro for those cases. The indirect call however slows
+%D down the other cases.
+%D
+%D \starttyping
+%D \def\p!dogetfirstfromcommalist [#1,#2]{\def\commalistelement{#1}}
+%D \def\p!dogetsecondfromcommalist[#1,#2,#3]{\def\commalistelement{#2}}
+%D \let\p!dogetotherfromcommalist=\getfromcommalist
+%D
+%D \def\getfromcommalist[#1]#2[#3]% optimized for 0,1,2
+%D {\ifcase#3\relax
+%D \let\commalistelement\empty
+%D \or
+%D \p!dogetfirstfromcommalist[#1,]%
+%D \or
+%D \p!dogetsecondfromcommalist[#1,,]%
+%D \else
+%D \p!dogetotherfromcommalist[#1][#3]%
+%D \fi}
+%D \stoptyping
+%D
+%D Even worse, this alternative does not strip preceding
+%D spaces, which is what we want. So, we stick to the slow
+%D alternative.
+
+%D Watertight (and efficient) solutions are hard to find, due
+%D to the handling of braces during parameters passing and
+%D scanning. Nevertheless:
+%D
+%D \startbuffer
+%D \def\dosomething#1{(#1=\commalistsize) }
+%D
+%D \getcommalistsize [\hbox{$a,b,c,d,e,f$}] \dosomething 1
+%D \getcommalistsize [{a,b,c,d,e,f}] \dosomething 1
+%D \getcommalistsize [{a,b,c},d,e,f] \dosomething 4
+%D \getcommalistsize [a,b,{c,d,e},f] \dosomething 4
+%D \getcommalistsize [a{b,c},d,e,f] \dosomething 4
+%D \getcommalistsize [{a,b}c,d,e,f] \dosomething 4
+%D \getcommalistsize [] \dosomething 0
+%D \getcommalistsize [{[}] \dosomething 1
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D reports:
+%D
+%D \getbuffer
+
+%D \macros
+%D {dogetcommalistelement,dogetcommacommandelement}
+%D
+%D For low level (fast) purposes, we can also use the next
+%D alternative, which can handle 8~elements at most.
+%D
+%D \starttyping
+%D \dogetcommalistelement1\from a,b,c\to\commalistelement
+%D \stoptyping
+
+\def\dodogetcommalistelement#1\from#2,#3,#4,#5,#6,#7,#8\to#9%
+ {\edef#9{\ifcase#1\relax\or#2\or#3\or#4\or#5\or#6\or#7\or#8\fi}}
+
+% maybe better:
+%
+% {\@EA\edef\@EA#9\@EA{\ifcase#1\relax\or#2\or#3\or#4\or#5\or#6\or#7\or#8\fi}}
+
+\def\dogetcommalistelement#1\from#2\to%
+ {\dodogetcommalistelement#1\from#2,,,,,,\to}
+
+% check sources
+
+\def\dogetcommacommandelement#1\from#2\to%
+ {\@EA\dodogetcommalistelement\@EA#1\@EA\from#2,,,,,,\to}
+
+%D \macros
+%D {dosingleargument,dodoubleargument,dotripleargument,
+%D doquadrupleargument,doquintupleargument,dosixtupleargument,
+%D doseventupleargument}
+%D
+%D When working with delimited arguments, spaces and
+%D lineendings can interfere. The next set of macros uses
+%D \TEX' internal scanner for grabbing everything between
+%D arguments. Forgive me the funny names.
+%D
+%D \starttyping
+%D \dosingleargument\commando = \commando[#1]
+%D \dodoubleargument\commando = \commando[#1][#2]
+%D \dotripleargument\commando = \commando[#1][#2][#3]
+%D \doquadrupleargument\commando = \commando[#1][#2][#3][#4]
+%D \doquintupleargument\commando = \commando[#1][#2][#3][#4][#5]
+%D \dosixtupleargument\commando = \commando[#1][#2][#3][#4][#5][#6]
+%D \doseventupleargument\command= \commando[#1][#2][#3][#4][#5][#6][#7]
+%D \stoptyping
+%D
+%D These macros are used in the following way:
+%D
+%D \starttyping
+%D \def\dosetupsomething[#1][#2]%
+%D {... #1 ... #2 ...}
+%D
+%D \def\setupsomething%
+%D {\dodoubleargument\dosetupsomething}
+%D \stoptyping
+%D
+%D The implementation can be surprisingly simple and needs no
+%D further explanation, like:
+%D
+%D \starttyping
+%D \def\dosingleargument#1[#2]%
+%D {#1[#2]}
+%D \def\dotripleargument#1[#2]#3[#4]#5[#6]%
+%D {#1[#2][#4][#6]}
+%D \def\doquintupleargument#1%
+%D {\def\dodoquintupleargument[##1]##2[##3]##4[##5]##6[##7]##8[##9]%
+%D {#1[##1][##3][##5][##7][##9]}%
+%D \dodoquintupleargument}
+%D \stoptyping
+%D
+%D Because \TEX\ accepts 9~arguments at most, we have to use
+%D two||step solution when getting five or more arguments.
+%D
+%D When developing more and more of the real \CONTEXT, we
+%D started using some alternatives that provided empty
+%D arguments (in fact optional ones) whenever the user failed
+%D to supply them. Because this more complicated macros enable
+%D us to do some checking, we reimplemented the non||empty
+%D ones.
+
+\def\dosingleargument {\chardef\expectedarguments 1 \dosingleempty }
+\def\dodoubleargument {\chardef\expectedarguments 2 \dodoubleempty }
+\def\dotripleargument {\chardef\expectedarguments 3 \dotripleempty }
+\def\doquadrupleargument {\chardef\expectedarguments 4 \doquadrupleempty }
+\def\doquintupleargument {\chardef\expectedarguments 5 \doquintupleempty }
+\def\dosixtupleargument {\chardef\expectedarguments 6 \dosixtupleempty }
+\def\doseventupleargument{\chardef\expectedarguments 7 \doseventupleempty}
+
+%D \macros
+%D {iffirstagument,ifsecondargument,ifthirdargument,
+%D iffourthargument,iffifthargument,ifsixthargument,
+%D ifseventhargument}
+%D
+%D We use some signals for telling the calling macros if all
+%D wanted arguments are indeed supplied by the user.
+
+\newif\iffirstargument
+\newif\ifsecondargument
+\newif\ifthirdargument
+\newif\iffourthargument
+\newif\iffifthargument
+\newif\ifsixthargument
+\newif\ifseventhargument
+
+%D \macros
+%D {dosingleempty,dodoubleempty,dotripleempty,
+%D doquadrupleempty,doquintupleempty,dosixtupeempty,
+%D doseventupleempty}
+%D
+%D The empty argument supplying macros mentioned before, look
+%D like:
+%D
+%D \starttyping
+%D \dosingleempty \command
+%D \dodoubleempty \command
+%D \dotripleempty \command
+%D \doquadrupleempty \command
+%D \doquintupleempty \command
+%D \dosixtupleempty \command
+%D \doseventupleempty\command
+%D \stoptyping
+%D
+%D So \type{\dodoubleempty} leades to:
+%D
+%D \starttyping
+%D \command[#1][#2]
+%D \command[#1][]
+%D \command[][]
+%D \stoptyping
+%D
+%D Depending of the generousity of the user. Afterwards one can
+%D use the \type{\if...argument} boolean. For novice: watch
+%D the stepwise doubling of \type{#}'s
+
+% idea: \ignorespaces afterwards
+
+\chardef\noexpectedarguments=0
+\chardef\expectedarguments =0
+
+\def\showargumenterror#1#2%
+ {\writestatus{systems}{#1 argument(s) expected in line #2}}
+
+% \long\def\dogetargument#1#2#3#4% redefined in mult-ini
+% {\doifnextcharelse{#1}
+% {\let\expectedarguments\noexpectedarguments
+% #3\dodogetargument}
+% {\ifnum\expectedarguments>\noexpectedarguments
+% \showargumenterror{\expectedarguments}
+% \fi
+% \let\expectedarguments\noexpectedarguments
+% #4\dodogetargument#1#2}}
+%
+% less to pass
+
+\def\doshowargumenterror
+ {\ifnum\expectedarguments>\noexpectedarguments
+ \showargumenterror{\number\expectedarguments}{\number\inputlineno}%
+ \fi
+ \noshowargumenterror}
+
+\def\noshowargumenterror
+ {\let\expectedarguments\noexpectedarguments}
+
+% \long\def\dogetargument#1#2#3#4%
+% {\doifnextcharelse#1%
+% {\noshowargumenterror#3\dodogetargument}
+% {\doshowargumenterror#4\dodogetargument#1#2}}
+%
+% faster ?
+
+\long\def\dogetargument#1#2#3#4%
+ {\let\charactertoken=#1%
+ \def\!!stringa{\noshowargumenterror#3\dodogetargument}%
+ \def\!!stringb{\doshowargumenterror#4\dodogetargument#1#2}%
+ \futurelet\nexttoken\inspectnextcharacter}
+
+\def\getsingleempty#1#2#3%
+ {\def\dodogetargument%
+ {#3}%
+ \dogetargument#1#2\firstargumenttrue\firstargumentfalse}
+
+\def\getdoubleempty#1#2#3%
+ {\def\dodogetargument#1##1#2%
+ {\def\dodogetargument%
+ {#3#1{##1}#2}%
+ \dogetargument#1#2\secondargumenttrue\secondargumentfalse}%
+ \dogetargument#1#2\firstargumenttrue\firstargumentfalse}
+
+\def\gettripleempty#1#2#3%
+ {\def\dodogetargument#1##1#2%
+ {\def\dodogetargument#1####1#2%
+ {\def\dodogetargument%
+ {#3#1{##1}#2%
+ #1{####1}#2}%
+ \dogetargument#1#2\thirdargumenttrue\thirdargumentfalse}%
+ \dogetargument#1#2\secondargumenttrue\secondargumentfalse}%
+ \dogetargument#1#2\firstargumenttrue\firstargumentfalse}
+
+\def\getquadrupleempty#1#2#3%
+ {\def\dodogetargument#1##1#2%
+ {\def\dodogetargument#1####1#2%
+ {\def\dodogetargument#1########1#2%
+ {\def\dodogetargument%
+ {#3#1{##1}#2%
+ #1{####1}#2%
+ #1{########1}#2}%
+ \dogetargument#1#2\fourthargumenttrue\fourthargumentfalse}%
+ \dogetargument#1#2\thirdargumenttrue\thirdargumentfalse}%
+ \dogetargument#1#2\secondargumenttrue\secondargumentfalse}%
+ \dogetargument#1#2\firstargumenttrue\firstargumentfalse}
+
+\def\getquintupleempty#1#2#3%
+ {\def\dodogetargument#1##1#2%
+ {\def\dodogetargument#1####1#2%
+ {\def\dodogetargument#1########1#2%
+ {\def\dodogetargument#1################1#2%
+ {\def\dodogetargument%
+ {#3#1{##1}#2%
+ #1{####1}#2%
+ #1{########1}#2%
+ #1{################1}#2}%
+ \dogetargument#1#2\fifthargumenttrue\fifthargumentfalse}%
+ \dogetargument#1#2\fourthargumenttrue\fourthargumentfalse}%
+ \dogetargument#1#2\thirdargumenttrue\thirdargumentfalse}%
+ \dogetargument#1#2\secondargumenttrue\secondargumentfalse}%
+ \dogetargument#1#2\firstargumenttrue\firstargumentfalse}
+
+\def\getsixtupleempty#1#2#3%
+ {\def\dodogetargument#1##1#2%
+ {\def\dodogetargument#1####1#2%
+ {\def\dodogetargument#1########1#2%
+ {\def\dodogetargument#1################1#2%
+ {\def\dodogetargument#1################################1#2%
+ {\def\dodogetargument%
+ {#3#1{##1}#2%
+ #1{####1}#2%
+ #1{########1}#2%
+ #1{################1}#2%
+ #1{################################1}#2}%
+ \dogetargument#1#2\sixthargumenttrue\sixthargumentfalse}%
+ \dogetargument#1#2\fifthargumenttrue\fifthargumentfalse}%
+ \dogetargument#1#2\fourthargumenttrue\fourthargumentfalse}%
+ \dogetargument#1#2\thirdargumenttrue\thirdargumentfalse}%
+ \dogetargument#1#2\secondargumenttrue\secondargumentfalse}%
+ \dogetargument#1#2\firstargumenttrue\firstargumentfalse}
+
+\def\getseventupleempty#1#2#3%
+ {\def\dodogetargument#1##1#2%
+ {\def\dodogetargument#1####1#2%
+ {\def\dodogetargument#1########1#2%
+ {\def\dodogetargument#1################1#2%
+ {\def\dodogetargument#1################################1#2%
+ {\def\dodogetargument#1################################%
+ ################################1#2%
+ {\def\dodogetargument%
+ {#3#1{##1}#2%
+ #1{####1}#2%
+ #1{########1}#2%
+ #1{################1}#2%
+ #1{################################1}#2%
+ #1{################################%
+ ################################1}#2}%
+ \dogetargument#1#2\seventhargumenttrue\seventhargumentfalse}%
+ \dogetargument#1#2\sixthargumenttrue\sixthargumentfalse}%
+ \dogetargument#1#2\fifthargumenttrue\fifthargumentfalse}%
+ \dogetargument#1#2\fourthargumenttrue\fourthargumentfalse}%
+ \dogetargument#1#2\thirdargumenttrue\thirdargumentfalse}%
+ \dogetargument#1#2\secondargumenttrue\secondargumentfalse}%
+ \dogetargument#1#2\firstargumenttrue\firstargumentfalse}
+
+\def\dosingleempty {\getsingleempty []}
+\def\dodoubleempty {\getdoubleempty []}
+\def\dotripleempty {\gettripleempty []}
+\def\doquadrupleempty {\getquadrupleempty []}
+\def\doquintupleempty {\getquintupleempty []}
+\def\dosixtupleempty {\getsixtupleempty []}
+\def\doseventupleempty{\getseventupleempty[]}
+
+%D Because some of these are called quite often, we will now
+%D replace the more general version by alternatives tuned for
+%D speed.
+
+\def\dosingleempty#1%
+ {\noshowargumenterror % \relax % prevents lookahead, brr
+ \doifnextoptionalelse
+ {\firstargumenttrue#1}
+ {\dosinglefakeempty#1}}
+
+\def\dodoubleempty#1%
+ {\noshowargumenterror % \relax % prevents lookahead, brr
+ \doifnextoptionalelse
+ {\dodoubletestempty#1}
+ {\dodoublefakeempty#1}}
+
+\def\dotripleempty#1%
+ {\noshowargumenterror % \relax % prevents lookahead, brr
+ \doifnextoptionalelse
+ {\dotripletestempty#1}
+ {\dotriplefakeempty#1}}
+
+\def\dosinglefakeempty#1%
+ {\firstargumentfalse#1[]}
+
+\def\dodoublefakeempty#1%
+ {\firstargumentfalse\secondargumentfalse#1[][]}
+
+\def\dotriplefakeempty#1%
+ {\firstargumentfalse\secondargumentfalse\thirdargumentfalse#1[][][]}
+
+\long\def\dodoubletestempty#1[#2]%
+ {\firstargumenttrue
+ \doifnextoptionalelse
+ {\secondargumenttrue #1[{#2}]}
+ {\secondargumentfalse#1[{#2}][]}}
+
+\long\def\dotripletestempty#1[#2]%
+ {\firstargumenttrue
+ \doifnextoptionalelse
+ {\dotripletestemptyx #1[{#2}]}
+ {\secondargumentfalse
+ \thirdargumentfalse #1[{#2}][][]}}
+
+\long\def\dotripletestemptyx#1[#2][#3]%
+ {\secondargumenttrue
+ \doifnextoptionalelse
+ {\thirdargumenttrue #1[{#2}][{#3}]}
+ {\thirdargumentfalse#1[{#2}][{#3}][]}}
+
+%D \macros
+%D {strippedcsname}
+%D
+%D The next macro can be very useful when using \type{\csname}
+%D like in:
+%D
+%D \starttyping
+%D \csname if\strippedcsname\something\endcsname
+%D \stoptyping
+%D
+%D This expands to \type{\ifsomething}.
+
+\def\strippedcsname%
+ {\expandafter\gobbleoneargument\string}
+
+%D \macros
+%D {complexorsimple,complexorsimpleempty}
+%D
+%D Setups can be optional. A command expecting a setup is
+%D prefixed by \type{\complex}, a command without one gets the
+%D prefix \type{\simple}. Commands like this can be defined by:
+%D
+%D \starttyping
+%D \complexorsimple\command
+%D \stoptyping
+%D
+%D When \type{\command} is followed by a \type{[setup]}, then
+%D
+%D \starttyping
+%D \complexcommand [setup]
+%D \stoptyping
+%D
+%D executes, else we get
+%D
+%D \starttyping
+%D \simplecommand
+%D \stoptyping
+%D
+%D An alternative for \type{\complexorsimple} is:
+%D
+%D \starttyping
+%D \complexorsimpleempty {command}
+%D \stoptyping
+%D
+%D Depending on the presence of \type{[setup]}, this one
+%D leads to one of:
+%D
+%D \starttyping
+%D \complexcommando [setup]
+%D \complexcommando []
+%D \stoptyping
+%D
+%D Many \CONTEXT\ commands started as complex or simple ones,
+%D but changed into more versatile (more object oriented) ones
+%D using the \type{\get..argument} commands.
+
+% This method is needed when traditional tex is used with
+% the efficient definition (marked **) below.
+
+% an old one:
+%
+% \def\setnameofcommand#1% handles {abc} as well as \abc
+% {\begingroup
+% \escapechar=-1
+% \globaldefs=0 % pretty important!
+% \xdef\nameofcommand{\string#1}%
+% \endgroup}
+%
+% \def\complexorsimple#1%
+% {\setnameofcommand{#1}%
+% \doifnextcharelse{[}
+% {\firstargumenttrue \getvalue{\s!complex\nameofcommand}}
+% {\firstargumentfalse\getvalue{\s!simple \nameofcommand}}}
+%
+% \def\complexorsimpleempty#1%
+% {\setnameofcommand{#1}%
+% \doifnextcharelse{[}
+% {\firstargumenttrue \getvalue{\s!complex\nameofcommand}}
+% {\firstargumentfalse\getvalue{\s!complex\nameofcommand}[]}}
+%
+% a newer one:
+
+\def\complexorsimple#1%
+ {% \relax % prevents lookahead, brrr
+ \doifnextoptionalelse
+ {\firstargumenttrue \csname\s!complex\strippedcsname#1\endcsname}
+ {\firstargumentfalse\csname\s!simple \strippedcsname#1\endcsname}}
+
+\def\complexorsimpleempty#1%
+ {% \relax % prevents lookahead, brrr
+ \doifnextoptionalelse
+ {\firstargumenttrue \csname\s!complex\strippedcsname#1\endcsname}
+ {\firstargumentfalse\csname\s!complex\strippedcsname#1\endcsname[]}}
+
+%D \macros
+%D {definecomplexorsimple,definecomplexorsimpleempty}
+%D
+%D The previous commands are used that often that we found it
+%D worthwile to offer two more alternatives. Watch the build
+%D in protection.
+
+% See earlier. Because we don't want \type {\simple..} and
+% \type {\complex..} commands to show up in expansions, we need
+% to pass them as \type {simple..} and \type {complex..}.
+
+% \beginTEX
+%
+% \def\definewithnameofcommand#1#2%
+% {\setnameofcommand{#2}%
+% \@EA\def\@EA#2\@EA{\@EA\donottest\@EA#1\@EA{\nameofcommand}}}
+%
+% \def\definecomplexorsimple%
+% {\definewithnameofcommand\complexorsimple}
+%
+% \def\definecomplexorsimpleempty%
+% {\definewithnameofcommand\complexorsimpleempty}
+%
+% \endTEX
+%
+% \beginETEX \protected
+%
+% \def\definecomplexorsimple#1%
+% {\normalprotected\def#1{\complexorsimple#1}}
+%
+% \def\definecomplexorsimpleempty#1%
+% {\normalprotected\def#1{\complexorsimpleempty#1}}
+%
+% \endETEX
+
+% However, since this one uses an idirect method, things go
+% okay (at the cost of extra macros).
+
+% \def\definecomplexorsimple#1%
+% {\unexpanded\def#1{\complexorsimple#1}}
+%
+% \def\definecomplexorsimpleempty#1%
+% {\unexpanded\def#1{\complexorsimpleempty#1}}
+%
+% faster, since no \strippedcsname needed in call, but more spacy
+
+\def\docomplexorsimple#1#2%
+ {\doifnextoptionalelse{\firstargumenttrue#1}{\firstargumentfalse#2}}
+
+\def\docomplexorsimpleempty#1%
+ {\doifnextoptionalelse{\firstargumenttrue#1}{\firstargumentfalse#1[]}}
+
+\def\definecomplexorsimple#1%
+ {\unexpanded\edef#1%
+ {\noexpand\docomplexorsimple
+ \@EA\noexpand\csname\s!complex\strippedcsname#1\endcsname
+ \@EA\noexpand\csname\s!simple \strippedcsname#1\endcsname}}
+
+\def\definecomplexorsimpleempty#1%
+ {\unexpanded\edef#1%
+ {\noexpand\docomplexorsimpleempty
+ \@EA\noexpand\csname\s!complex\strippedcsname#1\endcsname}}
+
+%D These commands are called as:
+%D
+%D \starttyping
+%D \definecomplexorsimple\command
+%D \stoptyping
+%D
+%D Of course, we must have available
+%D
+%D \starttyping
+%D \def\complexcommand[#1]{...}
+%D \def\simplecommand {...}
+%D \stoptyping
+%D
+%D Using this construction saves a few string now and then.
+
+%D \macros
+%D {definestartstopcommand}
+%D
+%D Those who get the creeps of expansion may skip the next
+%D one. It's one of the most recent additions and concerns
+%D \type{\start}||\type{\stop} pairs with complicated
+%D arguments.
+%D
+%D We won't go into details here, but the general form of
+%D this using this command is:
+%D
+%D \starttyping
+%D \definestartstopcommand\somecommand\e!specifier{arg}{arg}%
+%D {do something with arg}
+%D \stoptyping
+%D
+%D This expands to something like:
+%D
+%D \starttyping
+%D \def\somecommand arg \startspecifier arg \stopspecifier%
+%D {do something with arg}
+%D \stoptyping
+%D
+%D The arguments can be anything reasonable, but double
+%D \type{#}'s are needed in the specification part, like:
+%D
+%D \starttyping
+%D \definestartstopcommand\somecommand\e!specifier{[##1][##2]}{##3}%
+%D {do #1 something #2 with #3 arg}
+%D \stoptyping
+%D
+%D which becomes:
+%D
+%D \starttyping
+%D \def\somecommand[#1][#2]\startspecifier#3\stopspecifier%
+%D {do #1 something #2 with #3 arg}
+%D \stoptyping
+%D
+%D We will see some real applications of this command in the
+%D core modules.
+
+\def\definestartstopcommand#1#2#3#4% can be done with \expanded ot better, toks
+ {\def\!stringa{#3}% % but let's keep this unused one crappy
+ \def\!stringb{\e!start#2}%
+ \def\!stringc{#4}%
+ \def\!stringd{\e!stop#2}%
+ \@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA
+ \def\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA
+ #1\@EA\@EA\@EA\@EA\@EA\@EA\@EA
+ \!stringa\@EA\@EA\@EA
+ \csname\@EA\@EA\@EA\!stringb\@EA\@EA\@EA\endcsname\@EA
+ \!stringc
+ \csname\!stringd\endcsname}
+
+%D \macros
+%D {dosinglegroupempty,dodoublegroupempty,dotriplegroupempty,
+%D doquadruplegroupempty, doquintuplegroupempty}
+%D
+%D We've already seen some commands that take care of
+%D optional arguments between \type{[]}. The next two commands
+%D handle the ones with \type{{}}. They are called as:
+%D
+%D \starttyping
+%D \dosinglegroupempty \ineedONEargument
+%D \dodoublegroupempty \ineedTWOarguments
+%D \dotriplegroupempty \ineedTHREEarguments
+%D \doquadruplegroupempty \ineedFOURarguments
+%D \doquintuplegroupempty \ineedFIVEarguments
+%D \stoptyping
+%D
+%D where \type{\ineedONEargument} takes one and the others
+%D two and three arguments. These macro's were first needed in
+%D \PPCHTEX.
+%D
+%D \starttyping
+%D \def\dogetgroupargument#1#2% redefined in mult-ini
+%D {\def\nextnextargument%
+%D {\ifx\nextargument\bgroup
+%D \let\expectedarguments\noexpectedarguments
+%D \def\nextargument{#1\dodogetargument}%
+%D %\else\ifx\nextargument\lineending % this can be an option
+%D % \def\nextargument{\begingroup\def\\ {\endgroup\dogetgroupargument#1#2}\\}%
+%D %\else\ifx\nextargument\blankspace % but it may never be default
+%D % \def\nextargument{\begingroup\def\\ {\endgroup\dogetgroupargument#1#2}\\}%
+%D \else
+%D \ifnum\expectedarguments>\noexpectedarguments
+%D \writestatus
+%D {setup}
+%D {\the\expectedarguments\space argument(s) expected
+%D in line \the\inputlineno\space}%
+%D \fi
+%D \let\expectedarguments\noexpectedarguments
+%D \def\nextargument{#2\dodogetargument{}}%
+%D \fi%\fi\fi % so let's get rid of it
+%D \nextargument}%
+%D \futurelet\nextargument\nextnextargument}
+%D \stoptyping
+%D
+%D In order to catch \type {\nextargument}'s that expand to
+%D \type {\if} and friends, in practice we will use a
+%D slightly more complicated macro.
+
+\newtoks \everyrobusttest
+
+\everyrobusttest
+ {\let\if \relax
+ \let\ifcat \relax
+ \let\ifnum \relax
+ \let\ifdim \relax
+ \let\ifodd \relax
+ \let\ifvmode \relax
+ \let\ifhmode \relax
+ \let\ifmmode \relax
+ \let\ifinner \relax
+ \let\ifvoid \relax
+ \let\ifhbox \relax
+ \let\ifvbox \relax
+ \let\ifx \relax
+ \let\ifeof \relax
+ \let\iftrue \relax
+ \let\iffalse \relax
+ \let\ifcase \relax
+ \let\ifdefined \relax
+ \let\ifcsname \relax
+ \let\iffontchar \relax
+ \let\ifincsname \relax
+ \let\ifprimitive\relax
+ \let\ifabsnum \relax
+ \let\ifabsdim \relax
+ \let\else \relax
+ \let\or \relax
+ \let\fi \relax}
+
+\def\beginrobusttest
+ {\begingroup
+ \the\everyrobusttest}
+
+\let\endrobusttest\endgroup
+
+%D We can add additional definitions later when we have defined
+%D \type {\appendtoks}.
+
+\def \permitspacesbetweengroups{\chardef\@@permitspacesbetweengroups=0 }
+\def\dontpermitspacesbetweengroups{\chardef\@@permitspacesbetweengroups=1 }
+
+\dontpermitspacesbetweengroups
+
+% \def\dogetgroupargument#1#2%
+% {\def\nextnextargument%
+% {\normalifx\nextargument\bgroup
+% \endrobusttest
+% \noshowargumenterror
+% \def\nextargument{#1\dodogetargument}%
+% \normalelse
+% \normalifcase\@@permitspacesbetweengroups
+% \normalifx\nextargument\lineending
+% \endrobusttest
+% \def\nextargument{\begingroup\def\\ {\endgroup\dogetgroupargument#1#2}\\}%
+% \normalelse\normalifx\nextargument\blankspace
+% \endrobusttest
+% \def\nextargument{\begingroup\def\\ {\endgroup\dogetgroupargument#1#2}\\}%
+% \normalelse
+% \endrobusttest
+% \doshowargumenterror
+% \def\nextargument{#2\dodogetargument{}}%
+% \normalfi\normalfi
+% \normalelse
+% \endrobusttest
+% \doshowargumenterror
+% \def\nextargument{#2\dodogetargument{}}%
+% \normalfi
+% \normalfi
+% \nextargument}%
+% \beginrobusttest
+% \futurelet\nextargument\nextnextargument}
+
+\def\dodogetgroupargument
+ {\normalifx\nextargument\bgroup
+ \endrobusttest
+ \noshowargumenterror
+ \def\nextargument{\dogroupargumentyes\dodogetargument}%
+ \normalelse
+ \normalifcase\@@permitspacesbetweengroups
+ \normalifx\nextargument\lineending
+ \endrobusttest
+ \def\nextargument{\begingroup\def\\ {\endgroup\dogetgroupargument\dogroupargumentyes\dogroupargumentnop}\\}%
+ \normalelse\normalifx\nextargument\blankspace
+ \endrobusttest
+ \def\nextargument{\begingroup\def\\ {\endgroup\dogetgroupargument\dogroupargumentyes\dogroupargumentnop}\\}%
+ \normalelse
+ \endrobusttest
+ \doshowargumenterror
+ \def\nextargument{\dogroupargumentnop\dodogetargument{}}%
+ \normalfi\normalfi
+ \normalelse
+ \endrobusttest
+ \doshowargumenterror
+ \def\nextargument{\dogroupargumentnop\dodogetargument{}}%
+ \normalfi
+ \normalfi
+ \nextargument}%
+
+\def\dogetgroupargument#1#2%
+ {\let\dogroupargumentyes#1%
+ \let\dogroupargumentnop#2%
+ \beginrobusttest\futurelet\nextargument\dodogetgroupargument}
+
+\def\dosinglegroupempty#1%
+ {\def\dodogetargument%
+ {\dontpermitspacesbetweengroups
+ #1}%
+ \dogetgroupargument\firstargumenttrue\firstargumentfalse}
+
+\def\dodoublegroupempty#1%
+ {\def\dodogetargument##1%
+ {\def\dodogetargument%
+ {\dontpermitspacesbetweengroups
+ #1{##1}}%
+ \dogetgroupargument\secondargumenttrue\secondargumentfalse}%
+ \dogetgroupargument\firstargumenttrue\firstargumentfalse}
+
+\def\dotriplegroupempty#1%
+ {\def\dodogetargument##1%
+ {\def\dodogetargument####1%
+ {\def\dodogetargument%
+ {\dontpermitspacesbetweengroups
+ #1{##1}{####1}}%
+ \dogetgroupargument\thirdargumenttrue\thirdargumentfalse}%
+ \dogetgroupargument\secondargumenttrue\secondargumentfalse}%
+ \dogetgroupargument\firstargumenttrue\firstargumentfalse}
+
+\def\doquadruplegroupempty#1%
+ {\def\dodogetargument##1%
+ {\def\dodogetargument####1%
+ {\def\dodogetargument########1%
+ {\def\dodogetargument%
+ {\dontpermitspacesbetweengroups
+ #1{##1}{####1}{########1}}%
+ \dogetgroupargument\fourthargumenttrue\fourthargumentfalse}%
+ \dogetgroupargument\thirdargumenttrue\thirdargumentfalse}%
+ \dogetgroupargument\secondargumenttrue\secondargumentfalse}%
+ \dogetgroupargument\firstargumenttrue\firstargumentfalse}
+
+\def\doquintuplegroupempty#1%
+ {\def\dodogetargument##1%
+ {\def\dodogetargument####1%
+ {\def\dodogetargument########1%
+ {\def\dodogetargument################1%
+ {\def\dodogetargument%
+ {\dontpermitspacesbetweengroups
+ #1{##1}{####1}{########1}{################1}}%
+ \dogetgroupargument\fifthargumenttrue\fifthargumentfalse}%
+ \dogetgroupargument\fourthargumenttrue\fourthargumentfalse}%
+ \dogetgroupargument\thirdargumenttrue\thirdargumentfalse}%
+ \dogetgroupargument\secondargumenttrue\secondargumentfalse}%
+ \dogetgroupargument\firstargumenttrue\firstargumentfalse}
+
+%D These macros can explictly take care of spaces, which means
+%D that the next definition and calls are valid:
+%D
+%D \starttyping
+%D \def\test#1#2#3{[#1#2#3]}
+%D
+%D \dotriplegroupempty\test {a}{b}{c}
+%D \dotriplegroupempty\test {a}{b}
+%D \dotriplegroupempty\test {a}
+%D \dotriplegroupempty\test
+%D \dotriplegroupempty\test {a} {b} {c}
+%D \dotriplegroupempty\test {a} {b}
+%D \dotriplegroupempty\test
+%D {a}
+%D {b}
+%D \stoptyping
+%D
+%D And alike.
+
+%D \macros
+%D {firstofoneargument, firstoftwoarguments, firstofthreearguments
+%D secondoftwoarguments, secondofthreearguments,
+%D thirdofthreearguments}
+%D
+%D The next six macros (dedicated to Taco) can conveniently
+%D used to select arguments. Their names explain their
+%D functionality.
+
+\long\def\firstofoneargument#1{#1}
+
+\long\def\firstoftwoarguments #1#2{#1}
+\long\def\secondoftwoarguments#1#2{#2}
+
+\long\def\firstofthreearguments #1#2#3{#1}
+\long\def\secondofthreearguments#1#2#3{#2}
+\long\def\thirdofthreearguments #1#2#3{#3}
+
+\long\def\firstoffourarguments #1#2#3#4{#1}
+\long\def\secondoffourarguments#1#2#3#4{#2}
+\long\def\thirdoffourarguments #1#2#3#4{#3}
+\long\def\fourthoffourarguments#1#2#3#4{#4}
+
+\long\def\firstoffivearguments #1#2#3#4#5{#1}
+\long\def\secondoffivearguments#1#2#3#4#5{#2}
+\long\def\thirdoffivearguments #1#2#3#4#5{#3}
+\long\def\fourthoffivearguments#1#2#3#4#5{#4}
+\long\def\fifthoffivearguments #1#2#3#4#5{#5}
+
+\long\def\firstofsixarguments #1#2#3#4#5#6{#1}
+\long\def\secondofsixarguments#1#2#3#4#5#6{#2}
+\long\def\thirdofsixarguments #1#2#3#4#5#6{#3}
+\long\def\fourthofsixarguments#1#2#3#4#5#6{#4}
+\long\def\fifthofsixarguments #1#2#3#4#5#6{#5}
+\long\def\sixthofsixarguments #1#2#3#4#5#6{#6}
+
+%D \macros
+%D {globalletempty,letempty,letvalueempty,letgvalueempty}
+%D
+%D Trivial:
+
+\def\letempty #1{\let#1\empty}
+\def\globalletempty#1{\global\let#1\empty}
+
+\def\letvalueempty #1{\expandafter\let\csname#1\endcsname\empty}
+\def\letgvalueempty#1{\global\expandafter\let\csname#1\endcsname\empty}
+
+%D \macros
+%D {wait}
+%D
+%D The next macro hardly needs explanation. Because no
+%D nesting is to be expected, we can reuse \type{\wait} within
+%D \type{\wait} itself.
+
+\def\wait
+ {\begingroup
+ \read16 to \wait
+ \endgroup}
+
+%D \macros
+%D {writestring,writeline,writebanner,
+%D writestatus,statuswidth,normalwritestatus}
+%D
+%D Maybe one didn't notice, but we've already introduced a
+%D macro for showing messages. In the multi||lingual modules,
+%D we will also introduce a mechanism for message passing. For
+%D the moment we stick to the core macros:
+%D
+%D \starttyping
+%D \writestring {string}
+%D \writeline
+%D \writestatus {category} {message}
+%D \stoptyping
+%D
+%D Messages are formatted. One can provide the maximum with
+%D of the identification string with the macro \type
+%D {\statuswidth}.
+
+\chardef\statuswidth=15
+\chardef\statuswrite=16
+
+\ifx\writestring\undefined
+
+ \newtoks\everywritestring
+
+ \def\writedirect {\immediate\write\statuswrite}
+ \def\writeline {\writedirect{}}
+ \def\writestring#1{\begingroup\the\everywritestring\writedirect{#1}\endgroup}
+
+\fi
+
+%D First we present the normal \TEX\ variant, later we will
+%D show the \ETEX-way.
+
+
+\beginTEX
+
+ \newcount\statuscounter
+
+ \def\dosplitstatus#1%
+ {\advance\statuscounter \minusone
+ \ifcase\statuscounter
+ \expandafter\nosplitstatus
+ \else
+ \scratchtoks\@EA{\the\scratchtoks#1}%
+ \expandafter\dosplitstatus
+ \fi}
+
+ \def\nosplitstatus#1\end
+ {}
+
+ \def\writestatus#1#2%
+ {\begingroup
+ \scratchtoks\emptytoks
+ \statuscounter\statuswidth
+ \expandafter\dosplitstatus#1%
+ \space\space\space\space\space\space\space
+ \space\space\space\space\space\space\space
+ \space\space\space\space\space\space\end
+ \@EA\writestring\@EA{\the\scratchtoks\space:\space#2}%
+ \endgroup}
+
+\endTEX
+
+%D Because we're grouped, we could have initialized at forehand:
+%D
+%D \starttyping
+%D \statuscounter\statuswidth
+%D \stoptyping
+%D
+%D The next implementation saves only some 10 words of format
+%D memory, but we hardly gain any speed.
+%D
+%D \starttyping
+%D \def\dosplitstatus#1#2#3#4#5#6#7#8#9%
+%D {#1#2#3#4#5#6#7#8#9\dodosplitstatus}
+%D
+%D \def\dodosplitstatus#1#2#3#4#5#6\end
+%D {#1#2#3#4#5}
+%D
+%D \def\writestatus#1#2%
+%D {\writestring
+%D {\expandafter\dosplitstatus#1%
+%D \space\space\space\space\space
+%D \space\space\space\space\space
+%D \space\space\space\space\space\end
+%D \space:\space#2}}
+%D \stoptyping
+
+%D Okay then, more obscure but slightly faster: no split grabs
+%D the do split part and skipping the else branch has to happen
+%D anyway, so:
+
+\beginTEX
+
+ \def\dosplitstatus#1%
+ {\advance\statuscounter \minusone
+ \ifcase\statuscounter
+ \expandafter\nosplitstatus
+ \fi
+ \scratchtoks\@EA{\the\scratchtoks#1}%
+ \dosplitstatus}
+
+\endTEX
+
+%D The next (\ETEX\ specific) variant is twice as fast in 5/40
+%D situations, only gains some speed when multiple runs of large docs
+%D are done; fully expandable, no statuscounter needed, no restore (due
+%D to grouping) needed etc.
+
+\beginETEX \numexpr
+
+ \def\normalwritestatus#1#2%
+ {\writestring{\expandafter\dosplitstatus\expandafter\statuswidth#1%
+ \space\space\space\space\space\space\space
+ \space\space\space\space\space\space\space
+ \space\space\space\space\space\space\end
+ \space:\space#2}}
+
+ \def\dosplitstatus#1#2%
+ {\ifcase#1 \expandafter\nosplitstatus\fi#2%
+ \expandafter\dosplitstatus\expandafter{\the\numexpr#1+\minusone\relax}}
+
+ \def\nosplitstatus#1\end
+ {}
+
+\endETEX
+
+%D \macros
+%D {emptytoks}
+%D
+%D For this we need an empty token register, analogous
+%D to \type {\empty}.
+
+\newtoks\emptytoks
+
+%D \macros
+%D {debuggerinfo}
+%D
+%D For debugging purposes we can enhance macros with the
+%D next alternative. Here \type{debuggerinfo} stands for both
+%D a macro accepting two arguments and a boolean (in fact a
+%D few macro's too).
+
+\newif\ifdebuggerinfo
+
+\def\debuggerinfo#1#2%
+ {\ifdebuggerinfo
+ \writestatus{debugger}{#1:: #2}%
+ \fi}
+
+%D Finally we do what from now on will be done at the top of
+%D the files: we tell the user what we are loading.
+
+% \ifx\writestatus\undefined \let\writestatus\normalwritestatus \fi
+% \ifx\writebanner\undefined \def\writebanner{\writestring} \fi
+
+\ifx\normalwritestatus\undefined
+ % for use within latex
+ \ifx\writestatus\undefined
+ \def\writestatus#1#2{\immediate\write16{#1 : #2}}
+ \fi
+\else
+ \let\writestatus\normalwritestatus
+\fi
+
+\def\writebanner{\writestring}
+
+\writestatus{loading}{ConTeXt System Macros / General}
+
+%D Well, the real final command is the one that resets the
+%D unprotected characters \type{@}, \type{?} and \type{!}.
+
+\protect \endinput