summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/syst-aux.mkiv
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/mkiv/syst-aux.mkiv')
-rw-r--r--tex/context/base/mkiv/syst-aux.mkiv7463
1 files changed, 7463 insertions, 0 deletions
diff --git a/tex/context/base/mkiv/syst-aux.mkiv b/tex/context/base/mkiv/syst-aux.mkiv
new file mode 100644
index 000000000..5b7059ea9
--- /dev/null
+++ b/tex/context/base/mkiv/syst-aux.mkiv
@@ -0,0 +1,7463 @@
+%D \module
+%D [ file=syst-aux, % merge of syst-gen cum suis
+%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.
+
+%D There are some references to \LUA\ variants here but these concern (often old)
+%D experiments, moved from local test modules to here, cleaned up, but not really
+%D used. After all it's not that urgent and replacing helpers is a delicate process.
+%D Don't depend on it.
+
+\registerctxluafile{syst-aux}{1.001}
+
+% A dedicated primitive \ifvoidmacro\cs == \ifx\cs\empty is some 10% faster but
+% probably not that noticeable in practice. An \ifvoidtoks might make sense but we
+% don't test that often for it (and it's more work to implement in the engine).
+
+%D This is a stripped down combination of:
+%D
+%D \startitemize
+%D \item \type {syst-gen.tex}
+%D \item \type {syst-ext.tex}
+%D \item \type {syst-new.tex}
+%D \stopitemize
+%D
+%D We keep them around (for \MKII) so you can find comments, experiences,
+%D intermediate versions and cleaner variants there (and also non-\ETEX\ variants).
+%D
+%D Contrary to the older files, we now assume that this one is used in \CONTEXT\ and
+%D therefore we might also assume that some basic functionality is available.
+%D
+%D The original files contain previous implementations and notes about performance.
+%D This file will be stripped down in due time.
+%D
+%D Some of the macros here were only used in the bibliography module. They have been
+%D be moved to a separate syst module since the bib module is no longer using them.
+%D Some more will go away.
+
+\unprotect
+
+%D \macros
+%D {unexpanded}
+%D
+%D Because we use this module only in \MKIV, we have removed the old protection
+%D code.
+%D
+%D \starttyping
+%D \unexpanded\def\somecommand{... ... ...}
+%D \stoptyping
+%D
+%D This overloads the \ETEX\ primitive but as we already had an \MKII\ solution we
+%D keep the same name for a similar mechanism.
+
+\let\unexpanded\normalprotected
+
+% %D \macros
+% %D {expunded}
+% %D
+% %D \unexpanded\edef\TestA{zzz}
+% %D \edef\TestB{zzz}
+% %D
+% %D \doifelse {\TestA} {\TestB} {WRONG} {OKAY} \par
+% %D \doifelse {\TestA} {\expunded\TestB} {WRONG} {OKAY} \par
+% %D \doifelse {\expunded\TestA} {\TestB} {OKAY} {WRONG} \par
+% %D \doifelse {\expunded\TestA} {\expunded\TestB} {OKAY} {WRONG} \par
+
+% %def\expunded#1{\normalexpanded\expandafter{#1}}
+% \def\expunded#1{\expandafter\empty#1} % used within an edef anyway
+
+%D As we don't have namespace definers yet, we use a special one:
+
+\ifdefined\c_syst_helpers_n_of_namespaces
+
+ % lets plug in a better error message
+
+\else
+
+ \newcount\c_syst_helpers_n_of_namespaces \c_syst_helpers_n_of_namespaces\pluseight % 1-8 reserved for catcodes
+
+ \def\v_interfaces_prefix_template_system{\number \c_syst_helpers_n_of_namespaces>>}
+ %def\v_interfaces_prefix_template_system{\characters\c_syst_helpers_n_of_namespaces>>} % no \characters yet
+
+\fi
+
+\unexpanded\def\installsystemnamespace#1% maybe move this to syst-ini
+ {\ifcsname ??#1\endcsname
+ \writestatus\m!system{duplicate system namespace '#1'}\wait
+ \else
+ \global\advance\c_syst_helpers_n_of_namespaces\plusone
+ \expandafter\edef\csname ??#1\endcsname{\v_interfaces_prefix_template_system}%
+ \fi}
+
+%D \macros
+%D {normalspace}
+%D
+%D There is already \type{\space} but just to be sure we also provide:
+
+\def\normalspace{ }
+
+%D \macros
+%D {!!count, !!toks, !!dimen, !!box,
+%D !!width, !!height, !!depth, !!string, !!done}
+%D
+%D We define some more \COUNTERS\ and \DIMENSIONS. We also define some shortcuts to
+%D the local scatchregisters~0, 2, 4, 6 and~8.
+
+\newcount\!!counta \newtoks\!!toksa \newdimen\!!dimena \newbox\!!boxa
+\newcount\!!countb \newtoks\!!toksb \newdimen\!!dimenb \newbox\!!boxb
+\newcount\!!countc \newtoks\!!toksc \newdimen\!!dimenc \newbox\!!boxc
+\newcount\!!countd \newtoks\!!toksd \newdimen\!!dimend \newbox\!!boxd
+\newcount\!!counte \newtoks\!!tokse \newdimen\!!dimene \newbox\!!boxe
+\newcount\!!countf \newtoks\!!toksf \newdimen\!!dimenf \newbox\!!boxf
+ \newdimen\!!dimeng
+ \newdimen\!!dimenh
+ \newdimen\!!dimeni
+ \newdimen\!!dimenj
+ \newdimen\!!dimenk
+
+\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
+\newdimen\!!widthd \newdimen\!!heightd \newdimen\!!depthd
+
+\newif\if!!donea \newif\if!!doneb \newif\if!!donec
+\newif\if!!doned \newif\if!!donee \newif\if!!donef
+
+\def\!!zerocount {0} % alongside \zerocount
+\def\!!minusone {-1} % ...
+\def\!!plusone {1} % ...
+\def\!!plustwo {2} % ...
+\def\!!plusthree {3} % ...
+\def\!!plusfour {4} % ...
+\def\!!plusfive {5} % ...
+\def\!!plussix {6} % ...
+\def\!!plusseven {7} % ...
+\def\!!pluseight {8} % ...
+\def\!!plusnine {9} % alongside \plusnine
+
+\setnewconstant \uprotationangle 0
+\setnewconstant\rightrotationangle 90
+\setnewconstant \downrotationangle 180
+\setnewconstant \leftrotationangle 270
+
+\ifdefined\data \else \let\data \relax \fi % dep checker
+
+%D \macros
+%D {s!,c!,e!,p!,v!,@@,??}
+%D
+%D To save memory, we use constants (sometimes called variables). Redefining these
+%D constants can have disastrous results.
+
+\def\v!prefix! {v!}
+\def\c!prefix! {c!}
+\def\s!prefix! {s!}
+
+\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 These are not needed any more now that we have wide screens (and bytes come
+%D cheap).
+
+
+\let\@EA \singleexpandafter
+\let\@EAEAEA \doubleexpandafter
+\let\@EAEAEAEAEAEA\tripleexpandafter
+
+\let\@NX \noexpand
+\def\@EAEA {\expandafter\expandafter} % can often be avoided
+
+%D Sometimes we pass macros as arguments to commands that don't expand them
+%D before interpretation. Such commands can be enclosed with \type {\expanded},
+%D like:
+%D
+%D \starttyping
+%D \expanded{\setupsomething[\alfa]}
+%D \stoptyping
+%D
+%D Such situations occur for instance when \type{\alfa} is a commalist or when data
+%D stored in macros is fed to index of list commands. If needed, one should use
+%D \type{\noexpand} inside the argument. Later on we will meet some more clever
+%D alternatives to this command. Beware, only the simple one has \type {\noexpand}
+%D before its argument.
+
+\let\m_syst_helpers_expanded\empty
+
+\unexpanded\def\expanded#1%
+ {\xdef\m_syst_helpers_expanded{\noexpand#1}\m_syst_helpers_expanded}
+
+\unexpanded\def\startexpanded#1\stopexpanded
+ {\xdef\m_syst_helpers_expanded{#1}\m_syst_helpers_expanded}
+
+\let\stopexpanded\relax
+
+%D Recent \TEX\ engines have a primitive \type {\expanded} and we will use that when
+%D possible. After all, we can make not expandable macros now.
+
+% We cannot use the next variant as first we need to adapt \type {##}'s
+% in callers:
+%
+% \def\expanded#1%
+% {\normalexpanded{\noexpand#1}}
+%
+% \def\startexpanded#1\stopexpanded
+% {\normalexpanded{#1}}
+
+%D \macros
+%D {gobbleoneargument,gobble...arguments}
+%D
+%D The next set of macros just do nothing, except that they get rid of a number of
+%D arguments.
+
+\def\gobbleoneargument #1{}
+\def\gobbletwoarguments #1#2{}
+\def\gobblethreearguments#1#2#3{}
+\def\gobblefourarguments #1#2#3#4{}
+\def\gobblefivearguments #1#2#3#4#5{}
+\def\gobblesixarguments #1#2#3#4#5#6{}
+\def\gobblesevenarguments#1#2#3#4#5#6#7{}
+\def\gobbleeightarguments#1#2#3#4#5#6#7#8{}
+\def\gobbleninearguments #1#2#3#4#5#6#7#8#9{}
+\def\gobbletenarguments #1{\gobbleninearguments}
+
+\def\gobbleoneoptional [#1]{}
+\def\gobbletwooptionals [#1][#2]{}
+\def\gobblethreeoptionals[#1][#2][#3]{}
+\def\gobblefouroptionals [#1][#2][#3][#4]{}
+\def\gobblefiveoptionals [#1][#2][#3][#4][#5]{}
+
+%D Reserved macros for tests:
+
+\let\donothing\empty
+
+\let\m_syst_string_one \empty
+\let\m_syst_string_two \empty
+\let\m_syst_string_three\empty
+\let\m_syst_string_four \empty
+
+\let\m_syst_action_yes \empty
+\let\m_syst_action_nop \empty
+
+%D \macros
+%D {doifnextcharelse}
+%D
+%D When we started using \TEX\ in the late eighties, our first experiences with
+%D programming concerned a simple shell around \LATEX. The commands probably use
+%D most at \PRAGMA, are the itemizing ones. One of those few shell commands took
+%D care of an optional argument, that enabled us to specify what kind of item symbol
+%D we wanted. Without understanding anything we were able to locate a \LATEX\ macro
+%D that could be used to inspect the next character.
+%D
+%D It's this macro that the ancester of the next one presented here. It executes one
+%D of two actions, dependant of the next character. Disturbing spaces and line
+%D endings, which are 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}.
+
+\unexpanded\def\doifelsenextchar#1#2#3% #1 should not be {} !
+ {\let\charactertoken=#1% = needed here
+ \def\m_syst_action_yes{#2}%
+ \def\m_syst_action_nop{#3}%
+ \futurelet\nexttoken\syst_helpers_inspect_next_character}
+
+\let\doifnextcharelse\doifelsenextchar
+
+\def\syst_helpers_inspect_next_character
+ {\ifx\nexttoken\blankspace
+ \expandafter\syst_helpers_reinspect_next_character
+ \else
+ \expandafter\syst_helpers_inspect_next_character_indeed
+ \fi}
+
+\def\syst_helpers_inspect_next_character_indeed
+ {\ifx\nexttoken\charactertoken
+ \expandafter\m_syst_action_yes
+ \else
+ \expandafter\m_syst_action_nop
+ \fi}
+
+%D Because we will mostly use this macro for testing if the next character is \type
+%D {[}, we also make a slightly faster variant as it is not uncommon to have tens of
+%D thousands of calls to this test in a run. Of course it also is more convenient to
+%D read a trace then.
+
+% We could make variants without the \if_next_blank_space_token but the overhead is
+% only .1 sec on 3.5 for 10^6 tests and often that branch is not entered anyway. The
+% fast variants with less checking do make a difference however:
+
+% \testfeature{1000000}{\doifnextoptionalelse \gobbleoneargument\gobbleoneargument[} % 2.902s
+% \testfeature{1000000}{\doifnextoptionalcselse \gobbleoneargument\gobbleoneargument[} % 2.590s
+% \testfeature{1000000}{\doiffastoptionalcheckelse \gobbleoneargument\gobbleoneargument[} % 2.387s
+% \testfeature{1000000}{\doiffastoptionalcheckcselse\gobbleoneargument\gobbleoneargument[} % 2.168s
+
+\newif\if_next_blank_space_token
+
+\let\syst_helpers_next_optional_character_token=[
+
+\unexpanded\def\doifelsenextoptional#1#2%
+ {\def\m_syst_action_yes{#1}%
+ \def\m_syst_action_nop{#2}%
+ \let\if_next_blank_space_token\iffalse
+ \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
+
+\unexpanded\def\doifelsenextoptionalcs#1#2% \cs \cs (upto 10% faster)
+ {\let\m_syst_action_yes#1%
+ \let\m_syst_action_nop#2%
+ \let\if_next_blank_space_token\iffalse
+ \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
+
+\let\doifnextoptionalelse \doifelsenextoptional
+\let\doifnextoptionalcselse\doifelsenextoptionalcs
+
+\def\syst_helpers_inspect_next_optional_character
+ {\ifx\nexttoken\blankspace
+ \expandafter\syst_helpers_reinspect_next_optional_character
+ \else
+ \expandafter\syst_helpers_inspect_next_optional_character_indeed
+ \fi}
+
+\def\syst_helpers_inspect_next_optional_character_indeed
+ {\ifx\nexttoken\syst_helpers_next_optional_character_token
+ \expandafter\m_syst_action_yes
+ \else
+ \expandafter\m_syst_action_nop
+ \fi}
+
+\let\syst_helpers_next_bgroup_character_token\bgroup
+
+\unexpanded\def\doifelsenextbgroup#1#2%
+ {\def\m_syst_action_yes{#1}%
+ \def\m_syst_action_nop{#2}%
+ \let\if_next_blank_space_token\iffalse
+ \futurelet\nexttoken\syst_helpers_inspect_next_bgroup_character}
+
+\unexpanded\def\doifelsenextbgroupcs#1#2%
+ {\let\m_syst_action_yes#1%
+ \let\m_syst_action_nop#2%
+ \let\if_next_blank_space_token\iffalse
+ \futurelet\nexttoken\syst_helpers_inspect_next_bgroup_character}
+
+\let\doifnextbgroupelse \doifelsenextbgroup
+\let\doifnextbgroupcselse\doifelsenextbgroupcs
+
+\def\syst_helpers_inspect_next_bgroup_character
+ {\ifx\nexttoken\blankspace
+ \expandafter\syst_helpers_reinspect_next_bgroup_character
+ \else
+ \expandafter\syst_helpers_inspect_next_bgroup_character_indeed
+ \fi}
+
+\def\syst_helpers_inspect_next_bgroup_character_indeed
+ {\ifx\nexttoken\syst_helpers_next_bgroup_character_token
+ \expandafter\m_syst_action_yes
+ \else
+ \expandafter\m_syst_action_nop
+ \fi}
+
+\let\syst_helpers_next_parenthesis_character_token(
+
+\unexpanded\def\doifelsenextparenthesis#1#2%
+ {\def\m_syst_action_yes{#1}%
+ \def\m_syst_action_nop{#2}%
+ \let\if_next_blank_space_token\iffalse
+ \futurelet\nexttoken\syst_helpers_inspect_next_parenthesis_character}
+
+\let\doifnextparenthesiselse\doifelsenextparenthesis
+
+\def\syst_helpers_inspect_next_parenthesis_character
+ {\ifx\nexttoken\blankspace
+ \expandafter\syst_helpers_reinspect_next_parenthesis_character
+ \else
+ \expandafter\syst_helpers_inspect_next_parenthesis_character_indeed
+ \fi}
+
+\def\syst_helpers_inspect_next_parenthesis_character_indeed
+ {\ifx\nexttoken\syst_helpers_next_parenthesis_character_token
+ \expandafter\m_syst_action_yes
+ \else
+ \expandafter\m_syst_action_nop
+ \fi}
+
+%D The next one is handy in predictable situations:
+
+\unexpanded\def\doifelsefastoptionalcheck#1#2%
+ {\def\m_syst_action_yes{#1}%
+ \def\m_syst_action_nop{#2}%
+ \futurelet\nexttoken\syst_helpers_do_if_fast_optional_check_else}
+
+\unexpanded\def\doifelsefastoptionalcheckcs#1#2% \cs \cs
+ {\let\m_syst_action_yes#1%
+ \let\m_syst_action_nop#2%
+ \futurelet\nexttoken\syst_helpers_do_if_fast_optional_check_else}
+
+\let\doiffastoptionalcheckelse \doifelsefastoptionalcheck
+\let\doiffastoptionalcheckcselse\doifelsefastoptionalcheckcs
+
+\def\syst_helpers_do_if_fast_optional_check_else
+ {\ifx\nexttoken\syst_helpers_next_optional_character_token
+ \expandafter\m_syst_action_yes
+ \else
+ \expandafter\m_syst_action_nop
+ \fi}
+
+%D Here's one for skipping spaces and pars, handy for:
+%D
+%D \starttyping
+%D \hbox
+%D
+%D {a few lines later}
+%D \stoptyping
+
+% \unexpanded\def\assumelongusagecs#1%
+% {\let\m_syst_action#1%
+% \futurelet\nexttoken\syst_helpers_ignore_par_character}
+%
+% \def\syst_helpers_ignore_par_character
+% {\ifx\nexttoken\blankspace
+% \expandafter\syst_helpers_ignore_par_character_blankspace
+% \else
+% \expandafter\syst_helpers_ignore_par_character_followup
+% \fi}
+%
+% \def\syst_helpers_ignore_par_character_followup
+% {\ifx\nexttoken\par
+% \expandafter\syst_helpers_ignore_par_partoken
+% \else
+% \expandafter\m_syst_action
+% \fi}
+%
+% \def\syst_helpers_ignore_par_partoken
+% {\afterassignment\m_syst_action\let\nexttoken}
+
+\unexpanded\def\assumelongusagecs#1% can be relaxed when we have long support in \hbox etc
+ {\let\m_syst_action#1%
+ \futurelet\nexttoken\syst_helpers_ignore_spacing}
+
+\def\syst_helpers_ignore_spacing
+ {\ifx\nexttoken\blankspace
+ \singleexpandafter\syst_helpers_ignore_spacing_blankspace
+ \else\ifx\nexttoken\par
+ \doubleexpandafter\syst_helpers_ignore_spacing_partoken
+ \else
+ \doubleexpandafter\m_syst_action
+ \fi\fi}
+
+\def\syst_helpers_ignore_spacing_partoken\par
+ {\futurelet\nexttoken\syst_helpers_ignore_spacing}
+
+%D These macros use some auxiliary macros. Although we were able to program quite
+%D complicated things, I only understood these after rereading the \TEX book. The
+%D trick is in using a command with a one character name. Such commands differ from
+%D the longer ones in the fact that trailing spaces are {\em not} skipped. This
+%D enables us to indirectly define a long named macro that gobbles a space. In the
+%D first line we define \type {\blankspace}. Next we make \type {\:} equivalent to
+%D \type {\reinspect...}. This one||character command is expanded before the next
+%D \type {\def} comes into action. This way the space after \type {\:} becomes a
+%D delimiter of the longer named \type {\reinspectnextcharacter}.
+
+% try: \expandafter\def\firstofoneargument{\syst_helpers_reinspect_next_character} {...}
+
+\let\next\:
+
+\def\:{\let\blankspace= } \:
+
+\def\:{\syst_helpers_reinspect_next_character}
+\expandafter\def\: {\let\if_next_blank_space_token\iftrue\futurelet\nexttoken\syst_helpers_inspect_next_character}
+
+\def\:{\syst_helpers_reinspect_next_optional_character}
+\expandafter\def\: {\let\if_next_blank_space_token\iftrue\futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
+
+\def\:{\syst_helpers_reinspect_next_bgroup_character}
+\expandafter\def\: {\let\if_next_blank_space_token\iftrue\futurelet\nexttoken\syst_helpers_inspect_next_bgroup_character}
+
+\def\:{\syst_helpers_reinspect_next_parenthesis_character}
+\expandafter\def\: {\let\if_next_blank_space_token\iftrue\futurelet\nexttoken\syst_helpers_inspect_next_parenthesis_character}
+
+\def\:{\syst_helpers_ignore_spacing_blankspace}
+\expandafter\def\: {\futurelet\nexttoken\syst_helpers_ignore_spacing}
+
+\let\:\next
+
+%D \macros
+%D {setvalue,setgvalue,setevalue,setxvalue,
+%D letvalue,letgvalue,getvalue,resetvalue,
+%D undefinevalue,ignorevalue}
+%D
+%D \TEX's primitive \type {\csname} can be used to construct all kind of commands
+%D that cannot be defined with \type {\def} and \type {\let}. Every macro programmer
+%D sooner 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, which is mainly due to
+%D its object oriented and parameter 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\undefinevalue#1{\expandafter\let\csname#1\endcsname\undefined}
+\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}
+
+\unexpanded\def\getuvalue#1{\csname#1\endcsname}
+
+%D \macros
+%D {globallet,glet}
+%D
+%D In \CONTEXT\ of May 2000 using \type {\globallet} instead of the two
+%D tokens will save us some $300\times4=1200$ bytes of format file on a 32~bit
+%D system. Not that it matters much today. This shortcut is already defined:
+
+\unexpanded\def\glet{\global\let} \let\globallet\glet
+
+%D \macros
+%D {doifundefined,doifdefined,
+%D doifundefinedelse,doifdefinedelse,
+%D doifalldefinedelse}
+%D
+%D The standard way of testing if a macro is defined is comparing its meaning with
+%D another undefined one, usually \type{\undefined}. To garantee correct working of
+%D the next 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 table, which is of
+%D limited size. It is expected that \ETEX\ will offer a less memory||consuming
+%D alternative.
+
+%D Although it will probably never be a big problem, it is good to be aware of the
+%D difference between testing on a macro 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 \ifundefined\NameB ... \else ... \fi
+%D \stoptyping
+
+% \suppressifcsnameerror\plusone % already set
+
+\def\doifelseundefined#1%
+ {\ifcsname#1\endcsname
+ \expandafter\secondoftwoarguments\else\expandafter\firstoftwoarguments
+ \fi}
+
+\def\doifelsedefined#1%
+ {\ifcsname#1\endcsname
+ \expandafter\firstoftwoarguments\else\expandafter\secondoftwoarguments
+ \fi}
+
+\def\doifundefined#1%
+ {\ifcsname#1\endcsname
+ \expandafter\gobbleoneargument\else\expandafter\firstofoneargument
+ \fi}
+
+\def\doifdefined#1%
+ {\ifcsname#1\endcsname
+ \expandafter\firstofoneargument\else\expandafter\gobbleoneargument
+ \fi}
+
+\let\doifundefinedelse\doifelseundefined
+\let\doifdefinedelse \doifelsedefined
+
+%D \macros
+%D {letbeundefined}
+%D
+%D Testing for being undefined comes down to testing on \type {\relax} when we use
+%D \type {\csname}, but when using \type {\ifx}, we test on being \type
+%D {\undefined}! In \ETEX\ we have \type {\ifcsname} and that way of testing on
+%D existance is not the same as the one described here. Therefore we introduce:
+
+\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}
+
+%D Beware, being \type {\undefined} in \ETEX\ means that the macro {\em is} defined!
+%D
+%D When we were developing the scientific units module, we encountered different
+%D behavior in text and math mode, which was due to this grouping subtilities. We
+%D therefore decided to use \type{\begingroup} instead of \type{\bgroup}.
+
+\unexpanded\def\doifelsealldefined#1%
+ {\begingroup
+ \donetrue % we could use a reserved one and avoid the group
+ \processcommalist[#1]\syst_helpers_do_if_all_defined_else
+ \ifdone
+ \endgroup\expandafter\firstoftwoarguments
+ \else
+ \endgroup\expandafter\secondoftwoarguments
+ \fi}
+
+\let\doifalldefinedelse\doifelsealldefined
+
+\def\syst_helpers_do_if_all_defined_else#1%
+ {\ifcsname#1\endcsname\else
+ \donefalse
+ \expandafter\quitcommalist % added
+ \fi}
+
+%D \macros
+%D {doif,doifelse,doifnot}
+%D
+%D Programming in \TEX\ differs from programming in procedural languages like
+%D \MODULA. This means that one --- well, let me speek for myself --- tries to do
+%D the things in the well known way. Therefore the next set of \type{\ifthenelse}
+%D commands were between the first ones we needed. A few years later, the opposite
+%D became true: when programming in \MODULA, I sometimes miss handy things like
+%D grouping, runtime redefinition, expansion etc. While \MODULA\ taught me to
+%D 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
+
+\unexpanded\def\doif#1#2%
+ {\edef\m_syst_string_one{#1}%
+ \edef\m_syst_string_two{#2}%
+ \ifx\m_syst_string_one\m_syst_string_two
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\unexpanded\def\doifnot#1#2%
+ {\edef\m_syst_string_one{#1}%
+ \edef\m_syst_string_two{#2}%
+ \ifx\m_syst_string_one\m_syst_string_two
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\unexpanded\def\doifelse#1#2%
+ {\edef\m_syst_string_one{#1}%
+ \edef\m_syst_string_two{#2}%
+ \ifx\m_syst_string_one\m_syst_string_two
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+%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.
+
+\unexpanded\def\doifelseempty#1%
+ {\def\m_syst_string_one{#1}%
+ \ifx\m_syst_string_one\empty
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\let\doifemptyelse\doifelseempty
+
+\unexpanded\def\doifempty#1%
+ {\def\m_syst_string_one{#1}%
+ \ifx\m_syst_string_one\empty
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\unexpanded\def\doifnotempty#1%
+ {\def\m_syst_string_one{#1}%
+ \ifx\m_syst_string_one\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 set of strings.
+%D Depending on the result, some action is taken.
+%D
+%D \starttyping
+%D \doifinset {string} {string,...} {...}
+%D \doifnotinset {string} {string,...} {...}
+%D \doifinsetelse {string} {string,...} {then ...} {else ...}
+%D \stoptyping
+
+% !0nop=\doifinsetelse{ccc}{,}{yes}{nop}
+% !0nop=\doifinsetelse{ccc}{,,}{yes}{nop}
+% !0nop=\doifinsetelse{ccc}{,,,}{yes}{nop}
+
+% !1nop=\doifinsetelse{}{}{yes}{nop}
+% !2yes=\doifinsetelse{aaa}{bbb,ccc,ddd,aaa,eee}{yes}{nop}
+% !3nop=\doifinsetelse{aaa}{bbb}{yes}{nop}
+% !4yes=\doifinsetelse{aaa}{aaa}{yes}{nop}
+% !5nop=\doifinsetelse{aaaa}{bbb,ccc,ddd,aaa,eee}{yes}{nop}
+% !6nop=\doifinsetelse{}{}{yes}{nop}
+% !7nop=\doifinsetelse{}{aaa}{yes}{nop}
+% !8nop=\doifinsetelse{aaa}{}{yes}{nop}
+
+% !1=\doifinset{}{}{yes}
+% !2yes=\doifinset{aaa}{bbb,ccc,ddd,aaa,eee}{yes}
+% !3=\doifinset{aaa}{bbb}{yes}
+% !4yes=\doifinset{aaa}{aaa}{yes}
+% !5=\doifinset{}{}{yes}
+% !6=\doifinset{aaa}{}{yes}
+
+% !1yes=\doifnotinset{}{}{yes}
+% !2=\doifnotinset{aaa}{bbb,ccc,ddd,aaa,eee}{yes}
+% !3yes=\doifnotinset{aaa}{bbb}{yes}
+% !4=\doifnotinset{aaa}{aaa}{yes}
+% !5yes=\doifnotinset{}{}{yes}
+% !6yes=\doifnotinset{aaa}{}{yes}
+
+% \def\v_syst_helpers_right_optional_bracket{]}
+%
+% \def\syst_helpers_do_quit_if_item_in_set_else#1],\relax{\firstoftwoarguments}
+% \def\syst_helpers_do_quit_if_item_in_set #1],\relax{\firstofoneargument}
+% \def\syst_helpers_do_quit_if_item_not_in_set #1],\relax{\gobbleoneargument}
+%
+% \def\syst_helpers_re_do_if_in_set_else{\expandafter\syst_helpers_do_check_if_item_in_set_else\m_syst_string_two,],\relax}
+% \def\syst_helpers_re_do_if_in_set {\expandafter\syst_helpers_do_check_if_item_in_set \m_syst_string_two,],\relax}
+% \def\syst_helpers_re_do_if_not_in_set {\expandafter\syst_helpers_do_check_if_item_not_in_set \m_syst_string_two,],\relax}
+%
+% \unexpanded\def\doifelseinset#1% make this two step too
+% {\edef\m_syst_string_one{#1}%
+% \ifx\m_syst_string_one\empty
+% \expandafter\thirdofthreearguments
+% \else
+% \expandafter\syst_helpers_do_if_in_set_else
+% \fi}
+%
+% \let\doifinsetelse\doifelseinset
+%
+% \def\syst_helpers_do_if_in_set_else#1%
+% {\edef\m_syst_string_two{#1}%
+% \ifx\m_syst_string_two\empty
+% \expandafter\secondoftwoarguments
+% \else
+% \expandafter\syst_helpers_re_do_if_in_set_else
+% \fi}
+%
+% \unexpanded\def\doifinset#1%
+% {\edef\m_syst_string_one{#1}%
+% \ifx\m_syst_string_one\empty
+% \expandafter\gobbletwoarguments
+% \else
+% \expandafter\syst_helpers_do_if_in_set
+% \fi}
+%
+% \def\syst_helpers_do_if_in_set#1%
+% {\edef\m_syst_string_two{#1}%
+% \ifx\m_syst_string_two\empty
+% \expandafter\gobbleoneargument
+% \else
+% \expandafter\syst_helpers_re_do_if_in_set
+% \fi}
+%
+% \unexpanded\def\doifnotinset#1%
+% {\edef\m_syst_string_one{#1}%
+% \ifx\m_syst_string_one\empty
+% \expandafter\secondoftwoarguments
+% \else
+% \expandafter\syst_helpers_do_if_not_in_set
+% \fi}
+%
+% \def\syst_helpers_do_if_not_in_set#1%
+% {\edef\m_syst_string_two{#1}%
+% \ifx\m_syst_string_two\empty
+% \expandafter\firstofoneargument
+% \else
+% \expandafter\syst_helpers_re_do_if_not_in_set % ...]{true}
+% \fi}
+%
+% \def\syst_helpers_do_check_if_item_in_set_else#1,#2% #2 eats up preceding space
+% {\edef\m_syst_string_two{#1}%
+% \ifx\m_syst_string_two\empty
+% \expandafter\syst_helpers_do_check_if_item_in_set_else
+% \else
+% \expandafter\syst_helpers_do_do_check_if_item_in_set_else
+% \fi#2}
+%
+% \def\syst_helpers_do_do_check_if_item_in_set_else
+% {\ifx\m_syst_string_two\v_syst_helpers_right_optional_bracket
+% \expandafter\thirdofthreearguments
+% \else
+% \expandafter\syst_helpers_do_do_do_check_if_item_in_set_else
+% \fi}
+%
+% \def\syst_helpers_do_do_do_check_if_item_in_set_else
+% {\ifx\m_syst_string_one\m_syst_string_two
+% \expandafter\syst_helpers_do_quit_if_item_in_set_else
+% \else
+% \expandafter\syst_helpers_do_check_if_item_in_set_else
+% \fi}
+%
+% \def\syst_helpers_do_check_if_item_in_set#1,#2% #2 eats up preceding space
+% {\edef\m_syst_string_two{#1}%
+% \ifx\m_syst_string_two\empty
+% \expandafter\syst_helpers_do_check_if_item_in_set
+% \else
+% \expandafter\syst_helpers_do_do_check_if_item_in_set
+% \fi#2}
+%
+% \def\syst_helpers_do_do_check_if_item_in_set
+% {\ifx\m_syst_string_two\v_syst_helpers_right_optional_bracket
+% \expandafter\gobbletwoarguments
+% \else
+% \expandafter\syst_helpers_do_do_do_check_if_item_in_set
+% \fi}
+%
+% \def\syst_helpers_do_do_do_check_if_item_in_set
+% {\ifx\m_syst_string_one\m_syst_string_two
+% \expandafter\syst_helpers_do_quit_if_item_in_set
+% \else
+% \expandafter\syst_helpers_do_check_if_item_in_set
+% \fi}
+%
+% \def\syst_helpers_do_check_if_item_not_in_set#1,#2% #2 eats up preceding space
+% {\edef\m_syst_string_two{#1}%
+% \ifx\m_syst_string_two\empty
+% \expandafter\syst_helpers_do_check_if_item_not_in_set
+% \else
+% \expandafter\syst_helpers_do_do_check_if_item_not_in_set
+% \fi#2}
+%
+% \def\syst_helpers_do_do_check_if_item_not_in_set
+% {\ifx\m_syst_string_two\v_syst_helpers_right_optional_bracket
+% \expandafter\secondoftwoarguments
+% \else
+% \expandafter\syst_helpers_do_do_do_check_if_item_not_in_set
+% \fi}
+%
+% \def\syst_helpers_do_do_do_check_if_item_not_in_set
+% {\ifx\m_syst_string_one\m_syst_string_two
+% \expandafter\syst_helpers_do_quit_if_item_not_in_set
+% \else
+% \expandafter\syst_helpers_do_check_if_item_not_in_set
+% \fi}
+
+\unexpanded\def\doifelseinset#1#2{\clf_doifelseinset{#1}{#2}}
+\unexpanded\def\doifinset #1#2{\clf_doifinset {#1}{#2}}
+\unexpanded\def\doifnotinset #1#2{\clf_doifnotinset {#1}{#2}}
+
+\let\doifinsetelse\doifelseinset
+
+%D \macros
+%D {doifcommon,doifnotcommon,doifcommonelse}
+%D
+%D Probably the most time consuming tests are those that test for overlap in sets
+%D of strings.
+%D
+%D \starttyping
+%D \doifcommon {string,...} {string,...} {...}
+%D \doifnotcommon {string,...} {string,...} {...}
+%D \doifcommonelse {string,...} {string,...} {then ...} {else ...}
+%D \stoptyping
+
+% !1yes=\doifcommonelse{aaa,bbb,ccc}{aaa,bbb,ccc}{yes}{nop}
+% !2nop=\doifcommonelse{aaa,bbb,ccc}{ddd,eee,fff}{yes}{nop}
+% !3nop=\doifcommonelse{aaa}{ddd,eee,fff}{yes}{nop}
+% !4yes=\doifcommonelse{aaa}{aaa}{yes}{nop}
+% !5nop=\doifcommonelse{bbb}{aaa}{yes}{nop}
+% !6nop=\doifcommonelse{}{aaa,bbb,ccc}{yes}{nop}
+% !7nop=\doifcommonelse{aaa,bbb,ccc}{}{yes}{nop}
+% !8nop=\doifcommonelse{}{}{yes}{nop}
+
+% !9nop=\doifcommonelse{,,}{,,}{yes}{nop}
+% !9yes=\doifcommonelse{,a,}{,a,}{yes}{nop}
+% !9yes=\doifcommonelse{,,a,}{,a,}{yes}{nop}
+% !9yes=\doifcommonelse{,a,}{,,a,}{yes}{nop}
+% !9yes=\doifcommonelse{,a,}{,,,a,}{yes}{nop}
+% !9yes=\doifcommonelse{,,a,}{,,,a,}{yes}{nop}
+
+% \let\m_syst_common_a\empty
+% \let\m_syst_common_b\empty
+% \let\m_syst_common_c\empty
+%
+% \def\syst_helpers_do_quit_if_common_else#1],\relax#2],\relax{\firstoftwoarguments}
+%
+% \def\syst_helpers_do_check_if_common_else_one#1,#2% hm, why #2 here and passed at end
+% {\edef\m_syst_common_c{#1}%
+% \ifx\m_syst_common_c\v_syst_helpers_right_optional_bracket
+% \expandafter\thirdofthreearguments
+% \else
+% \expandafter\syst_helpers_do_common_check
+% \fi#2}
+%
+% \def\syst_helpers_do_check_if_common_else_two#1,#2% we can do an empty #1 check too
+% {\edef\commalistelement{#1}%
+% \ifx\commalistelement\v_syst_helpers_right_optional_bracket
+% \expandafter\syst_helpers_re_do_check_if_common_else_one
+% \else
+% \expandafter\syst_helpers_do_do_check_if_common_else_two
+% \fi#2}
+%
+% \def\syst_helpers_do_do_check_if_common_else_two
+% {\ifx\commalistelement\empty
+% \expandafter\syst_helpers_do_check_if_common_else_two
+% \else
+% \expandafter\syst_helpers_do_do_do_check_if_common_else_two
+% \fi}
+%
+% \def\syst_helpers_do_do_do_check_if_common_else_two
+% {\ifx\m_syst_common_c\commalistelement
+% \expandafter\syst_helpers_do_quit_if_common_else
+% \else
+% \expandafter\syst_helpers_do_check_if_common_else_two
+% \fi}
+%
+% \def\syst_helpers_re_do_check_if_common_else_one#1{\syst_helpers_do_check_if_common_else_one}
+%
+% \def\syst_helpers_do_common_check
+% {\expandafter\syst_helpers_do_check_if_common_else_two\m_syst_common_b,],\relax}%
+%
+% \def\syst_helpers_do_do_do_if_common_else
+% {\expandafter\syst_helpers_do_check_if_common_else_one\m_syst_common_a,],\relax}
+%
+% \def\syst_helpers_do_do_if_common_else#1#2#3#4%
+% {\edef\m_syst_common_a{#3}%
+% \edef\m_syst_common_b{#4}%
+% \ifx\m_syst_common_a\empty
+% \expandafter\secondoftwoarguments
+% \else\ifx\m_syst_common_b\empty
+% \expandafter\expandafter\expandafter\secondoftwoarguments
+% \else
+% \expandafter\expandafter\expandafter\syst_helpers_do_do_do_if_common_else
+% \fi\fi
+% #1#2}
+
+% \unexpanded\def\doifelsecommon{\syst_helpers_do_do_if_common_else\firstoftwoarguments\secondoftwoarguments}
+% \unexpanded\def\doifcommon {\syst_helpers_do_do_if_common_else\firstofoneargument \gobbleoneargument }
+% \unexpanded\def\doifnotcommon {\syst_helpers_do_do_if_common_else\gobbleoneargument \firstofoneargument }
+%
+% \let\doifcommonelse\doifelsecommon
+
+\unexpanded\def\doifelsecommon#1#2{\clf_doifelsecommon{#1}{#2}}
+\unexpanded\def\doifcommon #1#2{\clf_doifcommon {#1}{#2}}
+\unexpanded\def\doifnotcommon #1#2{\clf_doifnotcommon {#1}{#2}}
+
+\let\doifcommonelse\doifelsecommon
+
+%D \macros
+%D {processcommalist,processcommacommand,quitcommalist,
+%D processcommalistwithparameters}
+%D
+%D We've already seen some macros that take care of comma separated lists. Such
+%D 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 argument: the string.
+%D This command permits nesting and spaces after commas are skipped. Empty sets
+%D 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
+
+\installsystemnamespace{nextcommalevel}
+
+\def\syst_helpers_do_do_do_process_comma_item
+ {\csname\??nextcommalevel\the\commalevel\endcsname}
+
+\def\syst_helpers_do_do_process_comma_item
+ {\ifx\nexttoken\blankspace
+ \expandafter\syst_helpers_re_do_process_comma_item
+ \else
+ \expandafter\syst_helpers_do_do_process_comma_item_indeed
+ \fi}
+
+\def\syst_helpers_do_do_process_comma_item_indeed
+ {\ifx\nexttoken]%
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\syst_helpers_do_do_do_process_comma_item
+ \fi}
+
+\def\syst_helpers_do_process_comma_item
+ {\futurelet\nexttoken\syst_helpers_do_do_process_comma_item}
+
+%D Empty arguments are not processed. Empty items (\type {,,}) however are
+%D treated. We have to check for the special case \type {[{a,b,c}]}.
+
+\unexpanded\def\processcommalist[%
+ {\futurelet\nexttoken\syst_helpers_do_check_comma_item}
+
+\def\syst_helpers_do_check_comma_item
+ {\ifx\nexttoken]%
+ \expandafter\gobblethreearguments
+ \else
+ \expandafter\syst_helpers_do_process_comma_list
+ \fi
+ \relax} % this one preserved the next {}
+
+\def\syst_helpers_do_process_comma_list#1]#2%
+ {\global\advance\commalevel \plusone
+ \expandafter\def\csname\??nextcommalevel\the\commalevel\endcsname##1,%
+ {#2{##1}\syst_helpers_do_process_comma_item}%
+ \expandafter\syst_helpers_do_do_process_comma_item\gobbleoneargument#1,]\relax
+ \global\advance\commalevel \minusone }
+
+%D One way of quitting a commalist halfway is:
+
+\unexpanded\def\quitcommalist
+ {\begingroup\let\syst_helpers_do_process_comma_item\syst_helpers_do_quit_comma_list}
+
+\def\syst_helpers_do_quit_comma_list#1]%
+ {\endgroup}
+
+\unexpanded\def\quitprevcommalist
+ {\begingroup\let\syst_helpers_do_process_comma_item\syst_helpers_do_quit_prev_comma_list}
+
+\def\syst_helpers_do_quit_prev_comma_list#1]%
+ {\let\syst_helpers_do_process_comma_item\syst_helpers_do_quit_comma_list}
+
+%D The hack we used for checking the next character \type {\doifnextcharelse}
+%D is also used here.
+
+\let\next\:
+
+\def\:{\syst_helpers_re_do_process_comma_item} % \:not saved ?
+
+\expandafter\def\: {\futurelet\nexttoken\syst_helpers_do_do_process_comma_item}
+
+\let\:\next
+
+%D The previous examples lead to:
+%D
+%D \getbuffer
+
+%D When a list is saved in a macro, we can use a construction like:
+%D
+%D \starttyping
+%D \expandafter\processcommalist\expandafter[\list]\command
+%D \stoptyping
+%D
+%D Such solutions suit most situations, but we wanted a bit 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 use of
+%D this macro has its limits.
+
+\unexpanded\def\processcommacommand[#1]%
+ {\normalexpanded{\processcommalist[#1]}}
+
+%D The argument to \type{\command} is not delimited. Because we often
+%D 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
+
+\unexpanded\def\processcommalistwithparameters[#1]#2%
+ {\def\syst_helpers_do_process_comma_list_with_parameters##1{#2[##1]}%
+ \processcommalist[#1]\syst_helpers_do_process_comma_list_with_parameters}
+
+%D \macros
+%D {startprocesscommalist,startprocesscommacommand}
+%D
+%D Two more:
+
+\let\syst_helpers_comma_list_step\relax
+
+\unexpanded\def\startprocesscommalist[#1]#2\stopprocesscommalist
+ {\def\syst_helpers_comma_list_step##1{\def\currentcommalistitem{##1}#2}%
+ \processcommalist[#1]\syst_helpers_comma_list_step}
+
+\unexpanded\def\startprocesscommacommand[#1]#2\stopprocesscommacommand
+ {\def\syst_helpers_comma_list_step##1{\def\currentcommalistitem{##1}#2}%
+ \normalexpanded{\processcommalist[#1]}\syst_helpers_comma_list_step}
+
+\let\stopprocesscommalist \relax
+\let\stopprocesscommacommand\relax
+
+%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.
+
+\newcount\processlevel
+
+% obsolete: \def\expandactions{\let\expandedaction\edef} \expandactions (see mkii)
+
+\unexpanded\def\syst_helpers_do_compare_process_action_a[#1=>#2][#3]%
+ {\edef\m_syst_string_two{#1}%
+ \ifx\m_syst_string_two\s!default
+ \let\commalistelement\empty
+ #2%
+ \fi}
+
+% met \quitcommalist tot meer dan 25\% sneller
+
+\unexpanded\def\syst_helpers_do_compare_process_action_b[#1=>#2][#3]%
+ {\edef\m_syst_string_two{#1}%
+ \ifx\m_syst_string_one\m_syst_string_two
+ \def\commalistelement{#3}%
+ #2%
+ \expandafter\quitcommalist
+ \else\ifx\m_syst_string_two\s!unknown
+ \def\commalistelement{#3}% beware of loops
+ #2%
+ \fi\fi}
+
+\unexpanded\def\processaction[#1]#2[%
+ {\edef\m_syst_string_one{#1}%
+ \ifx\m_syst_string_one\empty
+ \let\syst_helpers_do_compare_process_action\syst_helpers_do_compare_process_action_a
+ \else
+ \let\syst_helpers_do_compare_process_action\syst_helpers_do_compare_process_action_b
+ \fi
+ \edef\syst_helpers_do_process_action##1{\syst_helpers_do_compare_process_action[##1][#1]}% expands #1
+ \processnextcommalist\relax\relax\syst_helpers_do_process_action[}
+
+\unexpanded\def\syst_helpers_do_compare_process_action_c[#1=>#2][#3]%
+ {\edef\m_syst_string_one{#1}%
+ \edef\m_syst_string_two{#3}%
+ \ifx\m_syst_string_one\m_syst_string_two
+ \def\commalistelement{#3}%
+ #2%
+ \expandafter\quitprevcommalist
+ \else
+ \edef\m_syst_string_one{#1}%
+ \ifx\m_syst_string_one\s!unknown
+ \def\commalistelement{#3}%
+ #2%
+ \fi
+ \fi}
+
+\unexpanded\def\processfirstactioninset[#1]%
+ {\edef\m_syst_string_one{#1}%
+ \ifx\m_syst_string_one\empty
+ \expandafter\processaction
+ \else
+ \expandafter\syst_helpers_process_first_action_in_set_indeed
+ \fi
+ [#1]}
+
+\unexpanded\def\syst_helpers_process_first_action_in_set_indeed[#1]#2[#3]%
+ {\def\syst_helpers_do_process_action##1%
+ {\def\syst_helpers_do_do_process_action####1{\syst_helpers_do_compare_process_action_c[####1][##1]}%
+ \processcommalist[#3]\syst_helpers_do_do_process_action}%
+ \normalexpanded{\processcommalist[#1]}\syst_helpers_do_process_action}
+
+\unexpanded\def\syst_helpers_do_compare_process_action_d[#1=>#2][#3]%
+ {\edef\m_syst_string_one{#1}%
+ \edef\m_syst_string_two{#3}%
+ \ifx\m_syst_string_one\m_syst_string_two
+ \def\commalistelement{#3}%
+ #2%
+ \expandafter\quitcommalist
+ \else
+ \edef\m_syst_string_one{#1}%
+ \ifx\m_syst_string_one\s!unknown
+ \def\commalistelement{#3}%
+ #2%
+ \fi
+ \fi}
+
+\installsystemnamespace{nextactionlevel}
+
+\unexpanded\def\syst_helpers_do_process_all_actions_in_set
+ {\csname\??nextactionlevel\the\processlevel\endcsname}
+
+\unexpanded\def\processallactionsinset[#1]%
+ {\edef\m_syst_string_one{#1}%
+ \ifx\m_syst_string_one\empty
+ \expandafter\processaction
+ \else
+ \expandafter\syst_helpers_process_all_actions_in_set_indeed
+ \fi
+ [#1]}
+
+\unexpanded\def\syst_helpers_process_all_actions_in_set_indeed[#1]#2[#3]%
+ {\advance\processlevel \plusone
+ \expandafter\def\csname\??nextactionlevel\the\processlevel\endcsname##1%
+ {\def\syst_helpers_do_do_process_action####1{\syst_helpers_do_compare_process_action_d[####1][##1]}%
+ \processcommalist[#3]\syst_helpers_do_do_process_action}%
+ \normalexpanded{\processcommalist[#1]}\syst_helpers_do_process_all_actions_in_set
+ \advance\processlevel\minusone}
+
+%D These macros use:
+
+\unexpanded\def\processnextcommalist#1#2#3[#4#5]%
+ {#1%
+ \let\nexttoken#4%
+ \global\advance\commalevel \plusone
+ \expandafter\def\csname\??nextcommalevel\the\commalevel\endcsname##1,%
+ {#3{##1}\syst_helpers_do_process_comma_item}%
+ \syst_helpers_do_do_process_comma_item#4#5,]\relax
+ \global\advance\commalevel\minusone
+ #2}
+
+%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.
+
+\let\firstcharacter \empty
+\let\remainingcharacters\empty
+
+\unexpanded\def\getfirstcharacter #1{\clf_getfirstcharacter{#1}}
+\unexpanded\def\doifelsefirstchar #1#2{\clf_doifelsefirstchar{#1}{#2}}
+\unexpanded\def\thefirstcharacter #1{\clf_thefirstcharacter{#1}}
+\unexpanded\def\theremainingcharacters#1{\clf_theremainingcharacters{#1}}
+
+\let\doiffirstcharelse\doifelsefirstchar
+
+%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
+
+\let\m_syst_sub_string\empty
+
+\unexpanded\def\doifelseinstring#1%
+ {\edef\m_syst_sub_string{#1}% expand #1 here
+ \ifx\m_syst_sub_string\empty
+ \expandafter\thirdofthreearguments
+ \else
+ \expandafter\syst_helpers_do_if_in_string_else_indeed
+ \fi}
+
+\let\doifinstringelse\doifelseinstring
+
+\unexpanded\def\syst_helpers_do_if_in_string_else_indeed#1%
+ {\syst_helpers_do_if_in_string_else\m_syst_sub_string{#1}%
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\unexpanded\def\doifinstring#1%%
+ {\edef\m_syst_sub_string{#1}% expand #1 here
+ \ifx\m_syst_sub_string\empty
+ \expandafter\gobbletwoarguments
+ \else
+ \expandafter\syst_helpers_do_if_in_string_indeed
+ \fi}
+
+\unexpanded\def\syst_helpers_do_if_in_string_indeed#1%
+ {\syst_helpers_do_if_in_string_else\m_syst_sub_string{#1}%
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\unexpanded\def\doifnotinstring#1%%
+ {\edef\m_syst_sub_string{#1}% expand #1 here
+ \ifx\m_syst_sub_string\empty
+ \expandafter\gobbletwoarguments
+ \else
+ \expandafter\syst_helpers_do_if_not_in_string_indeed
+ \fi}
+
+\unexpanded\def\syst_helpers_do_if_not_in_string_indeed#1%
+ {\syst_helpers_do_if_in_string_else\m_syst_sub_string{#1}%
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+% replaces prev
+
+\unexpanded\def\syst_helpers_do_if_in_string_else#1#2% ##2 can be {abc}
+ {\expandafter\def\expandafter\syst_helpers_do_do_if_in_string_else
+ \expandafter##\expandafter1#1##2##3\_e_o_s_{\unless\if##2@}% expand #1 here
+ \expandafter\syst_helpers_do_do_if_in_string_else\normalexpanded{#2#1}@@\_e_o_s_} % expand #2 here
+
+%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.
+
+\unexpanded\def\syst_helpers_do_if_in_csname_else#1#2%
+ {\def\syst_helpers_do_do_if_in_csname_else##1#1##2##3\_e_o_s_
+ {\unless\if##2@}%
+ \expandafter\syst_helpers_do_do_if_in_csname_else#2#1@@\_e_o_s_}
+
+\unexpanded\def\doifelseincsname#1#2%
+ {\normalexpanded{\syst_helpers_do_if_in_csname_else{#1}}{#2}%
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\let\doifincsnameelse\doifelseincsname
+
+%D \macros
+%D {doifnumberelse,doifnumber,doifnotnumber}
+%D
+%D The next macro executes a command depending of the outcome of a test on
+%D numerals. This is probably one of the fastest test possible, exept from
+%D a less robust 10||step \type {\if}||ladder or some tricky \type {\lcode}
+%D checking.
+%D
+%D \starttyping
+%D \doifnumberelse {string} {then ...} {else ...}
+%D \stoptyping
+%D
+%D The macro accepts \type {123}, \type {abc}, \type {{}}, \type {\getal} and
+%D \type {\the\count...}. This macro is a rather dirty one.
+
+\def\doifelsenumber#1% does not accept counters (fully expandable)
+ {\ifcase0\ifcase1#1\or\or\or\or\or\or\or\or\or\else1\fi\space
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+\let\doifnumberelse\doifelsenumber
+
+\def\doifnumber#1%
+ {\ifcase0\ifcase1#1\or\or\or\or\or\or\or\or\or\else1\fi\space
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\def\doifnotnumber#1%
+ {\ifcase0\ifcase1#1\or\or\or\or\or\or\or\or\or\else1\fi\space
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+%D \macros
+%D {setpercentdimen}
+%D
+%D \starttyping
+%D \scratchdimen=100pt \setpercentdimen\scratchdimen{10\letterpercent}
+%D \scratchdimen=100pt \setpercentdimen\scratchdimen{5pt}
+%D \scratchdimen \percentdimen \hsize {10\letterpercent}
+%D \stoptyping
+
+\def\percentdimen#1#2% dimen percentage (with %)
+ {\dimexpr\clf_percentageof{#2}\dimexpr#1\relax}
+
+\unexpanded\def\setpercentdimen#1#2% dimen percentage (with %)
+ {#1=\clf_percentageof{#2}\dimexpr#1\relax}
+
+%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 \rawdoifelseinset{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).
+
+\unexpanded\def\makerawcommalist[#1]#2% use \processnext ... here
+ {\def\syst_helpers_do_make_raw_comma_list##1% we don't expand ##1
+ {\ifx#2\empty
+ \def#2{##1}%
+ \else
+ \expandafter\def\expandafter#2\expandafter{#2,##1}%
+ \fi}%
+ \let#2\empty
+ \processcommalist[#1]\syst_helpers_do_make_raw_comma_list}
+
+\def\syst_helpers_raw_process_comma_item#1,#2% #2 eats up preceding space
+ {\if]#1\else
+ \csname\??nextcommalevel\the\commalevel\endcsname{#1}%
+ \expandafter\syst_helpers_raw_process_comma_item
+ \fi#2}
+
+\unexpanded\def\rawprocesscommalist[#1]#2% accepteert ook [\cs]
+ {\global\advance\commalevel \plusone
+ \expandafter\let\csname\??nextcommalevel\the\commalevel\endcsname#2%
+ \expandafter\syst_helpers_raw_process_comma_item#1,],% \relax
+ \global\advance\commalevel \minusone }
+
+\unexpanded\def\rawprocesscommacommand[#1]% not really needed
+ {\normalexpanded{\rawprocesscommalist[#1]}}
+
+% \def\rawdoifelseinset#1#2{\doifinstringelse{,#1,}{,#2,}}
+% \def\rawdoifinset #1#2{\doifinstring {,#1,}{,#2,}}
+
+\def\m_syst_two_commas{,,}
+
+\unexpanded\def\rawdoifelseinset#1%
+ {\edef\m_syst_sub_string{,#1,}% expand #1 here
+ \ifx\m_syst_sub_string\m_syst_two_commas
+ \expandafter\thirdofthreearguments
+ \else
+ \expandafter\syst_helpers_raw_do_if_in_set_else
+ \fi}
+
+\let\rawdoifinsetelse\rawdoifinsetelse
+
+\unexpanded\def\syst_helpers_raw_do_if_in_set_else#1%
+ {\syst_helpers_do_if_in_string_else\m_syst_sub_string{,#1,}%
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\unexpanded\def\rawdoifinset#1%
+ {\edef\m_syst_sub_string{,#1,}% expand #1 here
+ \ifx\m_syst_sub_string\m_syst_two_commas
+ \expandafter\gobbletwoarguments
+ \else
+ \expandafter\syst_helpers_raw_do_if_in_set
+ \fi}
+
+\unexpanded\def\syst_helpers_raw_do_if_in_set#1%%
+ {\syst_helpers_do_if_in_string_else\m_syst_sub_string{,#1,}%
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+%D Some more raw material:
+
+\def\syst_helpers_do_raw_process_action[#1][#2]%
+ {\def\syst_helpers_do_do_raw_process_action##1,#1=>##2,##3\_e_o_s_
+ {\if##3@\else
+ \def\m_syst_helpers_process_action{##2}%
+ \fi}%
+ \syst_helpers_do_do_raw_process_action,#2,#1=>,@\_e_o_s_}
+
+\unexpanded\def\rawprocessaction[#1]#2[#3]%
+ {\edef\m_syst_string_one{#1}%
+ \edef\m_syst_string_two{undefined}% better \!!undefined
+ \let\m_syst_helpers_process_action\m_syst_string_two
+ \ifx\m_syst_string_one\empty
+ \expandafter\syst_helpers_do_raw_process_action\expandafter[\s!default][#3]%
+ \else
+ \expandafter\syst_helpers_do_raw_process_action\expandafter[\m_syst_string_one][#3]%
+ \ifx\m_syst_helpers_process_action\m_syst_string_two
+ \expandafter\syst_helpers_do_raw_process_action\expandafter[\s!unknown][#3]%
+ \fi
+ \fi
+ \ifx\m_syst_helpers_process_action\m_syst_string_two
+ \else
+ \m_syst_helpers_process_action
+ \fi}
+
+%D When we process the list \type{a,b,c,d,e}, the raw routine takes over 30\% less
+%D time, when we feed $20+$ character strings we gain about 20\%. Alternatives which
+%D use \type{\futurelet} perform worse. Part of the speedup is due to the
+%D \type{\let} and \type{\expandafter} in the test.
+
+%D \macros
+%D {dosetvalue,dosetevalue,dosetgvalue,docopyvalue,doresetvalue,
+%D dogetvalue}
+%D
+%D When we are going to do assignments, we have to take multi||linguality into account.
+%D For the moment we keep 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 for use outside the
+%D assignment macros.
+
+\def\dosetvalue#1#2% #3
+ {\expandafter\def\csname#1#2\endcsname} % {#3}}
+
+\def\dosetevalue#1#2% #3
+ {\expandafter\edef\csname#1#2\endcsname} % {#3}}
+
+\def\dosetgvalue#1#2% #3
+ {\expandafter\gdef\csname#1#2\endcsname} % {#3}}
+
+\def\doresetvalue#1#2%
+ {\expandafter\let\csname#1#2\endcsname\empty}
+
+\def\doignorevalue#1#2#3%
+ {\expandafter\let\csname#1#2\endcsname\empty}
+
+\def\docopyvalue#1#2#3%
+ {\expandafter\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 concept of style file
+%D hacking, we took a considerable effort in building a parameterized system.
+%D Unfortunately there is a price to pay in terms of speed. Compared to other
+%D packages and taking the functionality of \CONTEXT\ into account, the total size
+%D of the format file is still very acceptable. Now 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 complain of it's missed. We
+%D will redefine this macro later on, when a more advanced message mechanism is
+%D implemented.
+
+\newif\iferrorisfatal
+
+\unexpanded\def\waitonfatalerror
+ {\iferrorisfatal\wait\fi}
+
+\unexpanded\def\showassignerror#1#2%
+ {\writestatus{setup}{missing or ungrouped '=' after '#1' in line #2}%
+ \waitonfatalerror}
+
+\unexpanded\def\doassignempty[#1][#2=#3]%
+ {\ifcsname#1#2\endcsname\else\dosetvalue{#1}{#2}{#3}\fi}
+
+%D \macros
+%D {getparameters,geteparameters,getgparameters,
+%D forgetparameters}
+%D
+%D Using the assignment commands directly is not our ideal of user friendly interfacing,
+%D so we take some further steps.
+%D
+%D \starttyping
+%D \getparameters [label] [...=...,...=...]
+%D \forgetparameters [label] [...=...,...=...]
+%D \stoptyping
+%D
+%D Again, the label identifies the category a variable belongs to. The second argument
+%D can be a comma separated 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 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{!!} pre||tagged user supplied
+%D variables from internal counterparts, we will introduce a slightly different tag
+%D in the multi||lingual modules. There we will use \type{c!} or \type{v!},
+%D depending on the context.
+%D
+%D By calling \type{doassign} directly, we save ourselves some argument passing
+%D and gain some speed. Whatever optimizations we do, this command will always be
+%D one of the bigger bottlenecks. The alternative \type{\geteparameters} --- it's
+%D funny to see that this alternative saw the light so lately --- can be used to do
+%D expanded assigments.
+
+\let\currentvalue\empty
+
+\unexpanded\def\getparameters {\dogetparameters\dosetvalue}
+\unexpanded\def\geteparameters {\dogetparameters\dosetevalue}
+\unexpanded\def\getgparameters {\dogetparameters\dosetgvalue}
+\unexpanded\def\getxparameters {\dogetparameters\dosetxvalue}
+\unexpanded\def\forgetparameters{\dogetparameters\doignorevalue}
+
+\let\getexpandedparameters\geteparameters
+
+\unexpanded\def\dogetparameters#1[#2]#3[#4%
+ {\if\noexpand#4]%
+ \expandafter\gobbleoneargument
+ \else
+ \let\setsomevalue#1%
+ \def\syst_helpers_get_parameters_assign{\syst_helpers_get_parameters_assign_indeed#2}%
+ \expandafter\syst_helpers_get_parameters
+ \fi#4}
+
+\def\syst_helpers_get_parameters#1]%
+ {\xprocesscommaitem#1,],\_e_o_p_}
+
+\def\syst_helpers_process_comma_item#1,#2% #2 takes space before ,
+ {\if,#1,% dirty trick for testing #1=empty
+ \expandafter\syst_helpers_process_comma_item
+ \else\if]#1%
+ \doubleexpandafter\gobbleoneargument
+ \else
+ \syst_helpers_get_parameters_assign\_e_o_p_#1==\empty\_e_o_p_
+ \doubleexpandafter\syst_helpers_process_comma_item
+ \fi\fi#2}
+
+\def\syst_helpers_assign_error#1#2#3%
+ {\showassignerror{#2}{\the\inputlineno\space(#1)}}
+
+\def\syst_helpers_get_parameters_assign_normal#1\_e_o_p_#2=#3=#4#5\_e_o_p_
+ {\ifx\empty#2\empty
+ \expandafter\syst_helpers_assign_error
+ \else\ifx#4\empty
+ \doubleexpandafter\syst_helpers_assign_error
+ \else
+ \doubleexpandafter\setsomevalue
+ \fi\fi
+ {#1}{#2}{#3}}
+
+\def\syst_helpers_get_parameters_assign_error#1\_e_o_p_#2=#3=#4#5\_e_o_p_
+ {\ifx\empty#2\empty
+ \expandafter\syst_helpers_assign_error
+ \else\ifx#4\empty
+ \doubleexpandafter\syst_helpers_assign_error
+ \else
+ \ifcsname#1#2\endcsname
+ \expandafter\let\expandafter\currentvalue\csname#1#2\endcsname
+ \else
+ \let\currentvalue\empty
+ \fi
+ \doubleexpandafter\setsomevalue
+ \fi\fi
+ {#1}{#2}{#3}}
+
+\let\syst_helpers_get_parameters_assign_indeed\syst_helpers_get_parameters_assign_normal
+
+\unexpanded\def\doassign [#1][#2]{\let\setsomevalue\dosetvalue \syst_helpers_get_parameters_assign_indeed#1\_e_o_p_#2==\empty\_e_o_p_}
+\unexpanded\def\doeassign [#1][#2]{\let\setsomevalue\dosetevalue \syst_helpers_get_parameters_assign_indeed#1\_e_o_p_#2==\empty\_e_o_p_}
+\unexpanded\def\undoassign[#1][#2]{\let\setsomevalue\doresetvalue\syst_helpers_get_parameters_assign_indeed#1\_e_o_p_#2==\empty\_e_o_p_}
+
+%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
+%D
+%D We can optimize this one if needed but it's not a core macro so hardly
+%D worth the trouble and tokens.
+
+\unexpanded\def\processassignmentlist[#1]#2% #2 == \command{key}{value]
+ {\def\syst_helpers_process_assignment_entry##1{#2}% {##2}{##3} % namespace is ignored
+ \dogetparameters\syst_helpers_process_assignment_entry[][#1]}
+
+\unexpanded\def\processassignmentcommand[#1]%
+ {\normalexpanded{\processassignmentlist[#1]}}
+
+\unexpanded\def\startprocessassignmentlist[#1]#2\stopprocessassignmentlist
+ {\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}%
+ \processassignmentlist[#1]\currentassignmentlistcommand}
+
+\unexpanded\def\startprocessassignmentcommand[#1]#2\stopprocessassignmentcommand
+ {\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}%
+ \normalexpanded{\processassignmentlist[#1]}\currentassignmentlistcommand}
+
+%D \macros{currentvalue}
+%D
+%D Just in case a \type{\getparameter} argument itself ends up inside a \type
+%D {\write} or other expandable location, our new macro needs a default value.
+%D
+%D \starttyping
+%D \getparameters[xxx][aaa=bbb]\par
+%D \getparameters[xxx][=bbb]\par
+%D \getparameters[xxx][aaa=]\par
+%D \getparameters[xxx][=]\par
+%D \getparameters[xxx][aaa]\par
+%D \stoptyping
+
+%D \macros
+%D {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
+%D Here we hook in the code (beware, this is the optimized get **):
+
+\def\syst_helpers_get_parameters_normal#1]%
+ {\syst_helpers_process_comma_item#1,],\_e_o_p_}
+
+\def\syst_helpers_get_parameters_expanded#1]%
+ {\let\dosetnvalue\setsomevalue
+ \let\setsomevalue\dosetevalue
+ \let\syst_helpers_get_parameters_assign_indeed\syst_helpers_get_parameters_assign_error
+ \let\setsomevalue\dosetevalue
+ \syst_helpers_process_comma_item#1,],\_e_o_p_
+ \let\syst_helpers_get_parameters_assign_indeed\syst_helpers_get_parameters_assign_normal
+ \let\setsomevalue\dosetnvalue
+ \let\syst_helpers_get_parameters\syst_helpers_get_parameters_normal
+ \let\currentvalue\empty}
+
+\let\syst_helpers_get_parameters\syst_helpers_get_parameters_normal % **
+
+\unexpanded\def\expandparameters
+ {\let\syst_helpers_get_parameters\syst_helpers_get_parameters_expanded}
+
+%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
+
+\unexpanded\def\getemptyparameters[#1]#2[#3]%
+ {\def\syst_helpers_get_empty_parameters##1{\doassignempty[#1][##1]}%
+ \processcommalist[#3]\syst_helpers_get_empty_parameters}
+
+%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.
+
+\unexpanded\def\copyparameters[#1]#2[#3]#4[#5]%
+ {\doifnot{#1}{#3}
+ {\def\syst_helpers_copy_parameter{\docopyvalue{#1}{#3}}% ##1
+ \processcommalist[#5]\syst_helpers_copy_parameter}}
+
+%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\syst_helpers_check_parameters#1=#2#3\_e_o_s_
+ {\if#2@\parametersfalse\else\parameterstrue\fi}
+
+\unexpanded\def\checkparameters[#1]%
+ {\syst_helpers_check_parameters#1=@@\_e_o_s_}
+
+%D \macros
+%D {getfromcommalist,getfromcommacommand,
+%D commalistelement,
+%D getcommalistsize,getcommacommandsize}
+%D
+%D It's possible to get an element from a commalist or a command representing
+%D 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 difference between
+%D \type {\processcomma...}. The found string is stored in \type
+%D {\commalistelement}.
+%D
+%D We can calculate the size of a comma separated list by 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 \type {\commalistsize}
+%D (not a \COUNTER).
+
+\newcount\commalistcounter
+
+\def\commalistsize{0}
+
+\def\syst_helpers_get_comma_list_size#1%
+ {\advance\commalistcounter\plusone}
+
+\unexpanded\def\getcommalistsize#1]% don't loose [{#1}]
+ {\commalistcounter\zerocount
+ \processcommalist#1]\syst_helpers_get_comma_list_size % was [{#1}]
+ \edef\commalistsize{\the\commalistcounter}}
+
+% \def\getcommacommandsize[#1]%
+% {\edef\commacommand{#1}%
+% \scratchtoks\expandafter{\expandafter[\commacommand]}%
+% \expandafter\getcommalistsize\the\scratchtoks }
+
+\unexpanded\def\getcommacommandsize[#1]%
+ {\normalexpanded{\getcommalistsize[#1]}}
+
+\def\syst_helpers_get_from_comma_list#1%
+ {\advance\commalistcounter \minusone
+ \ifcase\commalistcounter
+ \def\commalistelement{#1}%
+ \expandafter\quitcommalist
+ \fi}
+
+\unexpanded\def\getfromcommalist[#1]#2[#3]%
+ {\let\commalistelement\empty
+ \commalistcounter#3\relax
+ \processcommalist[#1]\syst_helpers_get_from_comma_list}
+
+\unexpanded\def\getfromcommacommand[#1]%
+ {\normalexpanded{\getfromcommalist[#1]}}
+
+%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\syst_helpers_get_comma_list_element#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}}
+
+\def\dogetcommacommandelement#1\from#2\to%
+ {\expandafter\syst_helpers_get_comma_list_element\expandafter#1\expandafter\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 \unexpanded\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.
+
+% no longer a mesage:
+%
+% \unexpanded\def\dosingleargument {\let\expectedarguments\plusone \dosingleempty }
+% \unexpanded\def\dodoubleargument {\let\expectedarguments\plustwo \dodoubleempty }
+% \unexpanded\def\dotripleargument {\let\expectedarguments\plusthree \dotripleempty }
+% \unexpanded\def\doquadrupleargument {\let\expectedarguments\plusfour \doquadrupleempty }
+% \unexpanded\def\doquintupleargument {\let\expectedarguments\plusfive \doquintupleempty }
+% \unexpanded\def\dosixtupleargument {\let\expectedarguments\plussix \dosixtupleempty }
+% \unexpanded\def\doseventupleargument{\let\expectedarguments\plusseven \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 like:
+%D
+%D \starttyping
+%D \dosingleempty \command
+%D \dodoubleempty \command
+%D \dotripleempty \command
+%D \doquadrupleempty \command
+%D \doquintupleempty \command
+%D \dosixtuple_empty \command
+%D \doseventupleempty\command
+%D \stoptyping
+%D
+%D So \type{\dodoubleempty} leads 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
+
+\setnewconstant\noexpectedarguments\zerocount
+\setnewconstant\expectedarguments \zerocount
+
+\unexpanded\def\showargumenterror#1#2%
+ {\writestatus{system}{\number#1 argument(s) expected in line #2}}
+
+\unexpanded\def\syst_helpers_argument_error
+ {\ifnum\expectedarguments>\noexpectedarguments
+ \showargumenterror{\number\expectedarguments}{\number\inputlineno}%
+ \fi
+ \syst_helpers_argument_reset}
+
+\unexpanded\def\syst_helpers_argument_reset
+ {\let\expectedarguments\noexpectedarguments}
+
+% \def\test[#1]{(#1)}
+%
+% \dosingleempty\test[] xxx\par
+% \dosingleempty\test xxx\par
+%
+% \def\test[#1][#2]{(#1,#2)}
+%
+% \dodoubleempty\test[][] xxx\par
+% \dodoubleempty\test[] xxx\par
+% \dodoubleempty\test xxx\par
+%
+% \def\test[#1][#2][#3]{(#1,#2,#3)}
+%
+% \dotripleempty\test[][][] xxx\par
+% \dotripleempty\test[][] xxx\par
+% \dotripleempty\test[] xxx\par
+% \dotripleempty\test xxx\par
+
+%D Single:
+
+\unexpanded\def\dosingleempty#1%
+ {\syst_helpers_argument_reset
+ \doifelsenextoptional
+ {\firstargumenttrue#1}%
+ {\syst_helpers_single_empty_one_nop#1}}
+
+\def\syst_helpers_single_empty_one_nop#1%
+ {\firstargumentfalse
+ #1[]}
+
+%D Double
+
+\unexpanded\def\dodoubleempty#1%
+ {\syst_helpers_argument_reset
+ \doifelsenextoptional
+ {\syst_helpers_double_empty_one_yes#1}%
+ {\syst_helpers_double_empty_one_nop#1}}
+
+\def\syst_helpers_double_empty_one_yes#1[#2]%
+ {\firstargumenttrue
+ \doifelsenextoptional
+ {\secondargumenttrue#1[{#2}]}%
+ {\syst_helpers_double_empty_two_nop#1{#2}}}
+
+\def\syst_helpers_double_empty_one_nop#1%
+ {\firstargumentfalse
+ \secondargumentfalse
+ #1[][]}
+
+\def\syst_helpers_double_empty_two_nop
+ {\secondargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_double_empty_one_spaced
+ \else
+ \expandafter\syst_helpers_double_empty_one_normal
+ \fi}
+
+\def\syst_helpers_double_empty_one_spaced#1#2{#1[{#2}][] }
+\def\syst_helpers_double_empty_one_normal#1#2{#1[{#2}][]}
+
+% Three
+
+\unexpanded\def\dotripleempty#1%
+ {\syst_helpers_argument_reset
+ \doifelsenextoptional
+ {\syst_helpers_triple_empty_one_yes#1}%
+ {\syst_helpers_triple_empty_one_nop#1}}
+
+\def\syst_helpers_triple_empty_one_yes#1[#2]%
+ {\firstargumenttrue
+ \doifelsenextoptional
+ {\syst_helpers_triple_empty_two_yes#1{#2}}%
+ {\syst_helpers_triple_empty_two_nop#1{#2}}}
+
+\def\syst_helpers_triple_empty_two_yes#1#2[#3]%
+ {\secondargumenttrue
+ \doifelsenextoptional
+ {\thirdargumenttrue#1[{#2}][{#3}]}%
+ {\syst_helpers_triple_empty_three_nop#1{#2}{#3}}}
+
+\def\syst_helpers_triple_empty_one_nop#1%
+ {\firstargumentfalse
+ \secondargumentfalse
+ \thirdargumentfalse
+ #1[][][]}
+
+\def\syst_helpers_triple_empty_two_nop
+ {\secondargumentfalse
+ \thirdargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_triple_empty_two_spaced
+ \else
+ \expandafter\syst_helpers_triple_empty_two_normal
+ \fi}
+
+\def\syst_helpers_triple_empty_three_nop
+ {\thirdargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_triple_empty_three_spaced
+ \else
+ \expandafter\syst_helpers_triple_empty_three_normal
+ \fi}
+
+\def\syst_helpers_triple_empty_two_spaced #1#2{#1[{#2}][][] }
+\def\syst_helpers_triple_empty_two_normal #1#2{#1[{#2}][][]}
+\def\syst_helpers_triple_empty_three_spaced#1#2#3{#1[{#2}][{#3}][] }
+\def\syst_helpers_triple_empty_three_normal#1#2#3{#1[{#2}][{#3}][]}
+
+%D Four:
+
+\unexpanded\def\doquadrupleempty#1%
+ {\syst_helpers_argument_reset
+ \doifelsenextoptional
+ {\syst_helpers_quadruple_empty_one_yes#1}%
+ {\syst_helpers_quadruple_empty_one_nop#1}}
+
+\def\syst_helpers_quadruple_empty_one_yes#1[#2]%
+ {\firstargumenttrue
+ \doifelsenextoptional
+ {\syst_helpers_quadruple_empty_two_yes#1{#2}}%
+ {\syst_helpers_quadruple_empty_two_nop#1{#2}}}
+
+\def\syst_helpers_quadruple_empty_two_yes#1#2[#3]%
+ {\secondargumenttrue
+ \doifelsenextoptional
+ {\syst_helpers_quadruple_empty_three_yes#1{#2}{#3}}%
+ {\syst_helpers_quadruple_empty_three_nop#1{#2}{#3}}}
+
+\def\syst_helpers_quadruple_empty_three_yes#1#2#3[#4]%
+ {\thirdargumenttrue
+ \doifelsenextoptional
+ {\fourthargumenttrue#1[{#2}][{#3}][{#4}]}%
+ {\syst_helpers_quadruple_empty_four_nop#1{#2}{#3}{#4}}}
+
+\def\syst_helpers_quadruple_empty_one_nop#1%
+ {\firstargumentfalse
+ \secondargumentfalse
+ \thirdargumentfalse
+ \fourthargumentfalse
+ #1[][][][]}
+
+\def\syst_helpers_quadruple_empty_two_nop
+ {\secondargumentfalse
+ \thirdargumentfalse
+ \fourthargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_quadruple_empty_two_spaced
+ \else
+ \expandafter\syst_helpers_quadruple_empty_two_normal
+ \fi}
+
+\def\syst_helpers_quadruple_empty_three_nop
+ {\thirdargumentfalse
+ \fourthargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_quadruple_empty_three_spaced
+ \else
+ \expandafter\syst_helpers_quadruple_empty_three_normal
+ \fi}
+
+\def\syst_helpers_quadruple_empty_four_nop
+ {\fourthargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_quadruple_empty_four_spaced
+ \else
+ \expandafter\syst_helpers_quadruple_empty_four_normal
+ \fi}
+
+\def\syst_helpers_quadruple_empty_two_spaced #1#2{#1[{#2}][][][] }
+\def\syst_helpers_quadruple_empty_two_normal #1#2{#1[{#2}][][][]}
+\def\syst_helpers_quadruple_empty_three_spaced #1#2#3{#1[{#2}][{#3}][][] }
+\def\syst_helpers_quadruple_empty_three_normal #1#2#3{#1[{#2}][{#3}][][]}
+\def\syst_helpers_quadruple_empty_four_spaced #1#2#3#4{#1[{#2}][{#3}][{#4}][] }
+\def\syst_helpers_quadruple_empty_four_normal #1#2#3#4{#1[{#2}][{#3}][{#4}][]}
+
+%D Five:
+
+\unexpanded\def\doquintupleempty#1%
+ {\syst_helpers_argument_reset
+ \doifelsenextoptional
+ {\syst_helpers_quintuple_empty_one_yes#1}%
+ {\syst_helpers_quintuple_empty_one_nop#1}}
+
+\def\syst_helpers_quintuple_empty_one_yes#1[#2]%
+ {\firstargumenttrue
+ \doifelsenextoptional
+ {\syst_helpers_quintuple_empty_two_yes#1{#2}}%
+ {\syst_helpers_quintuple_empty_two_nop#1{#2}}}
+
+\def\syst_helpers_quintuple_empty_two_yes#1#2[#3]%
+ {\secondargumenttrue
+ \doifelsenextoptional
+ {\syst_helpers_quintuple_empty_three_yes#1{#2}{#3}}%
+ {\syst_helpers_quintuple_empty_three_nop#1{#2}{#3}}}
+
+\def\syst_helpers_quintuple_empty_three_yes#1#2#3[#4]%
+ {\thirdargumenttrue
+ \doifelsenextoptional
+ {\syst_helpers_quintuple_empty_four_yes#1{#2}{#3}{#4}}%
+ {\syst_helpers_quintuple_empty_four_nop#1{#2}{#3}{#4}}}
+
+\def\syst_helpers_quintuple_empty_four_yes#1#2#3#4[#5]%
+ {\fourthargumenttrue
+ \doifelsenextoptional
+ {\fifthargumenttrue#1[{#2}][{#3}][{#4}][{#5}]}%
+ {\syst_helpers_quintuple_empty_five_nop#1{#2}{#3}{#4}{#5}}}
+
+\def\syst_helpers_quintuple_empty_one_nop#1%
+ {\firstargumentfalse
+ \secondargumentfalse
+ \thirdargumentfalse
+ \fourthargumentfalse
+ \fifthargumentfalse
+ #1[][][][][]}
+
+\def\syst_helpers_quintuple_empty_two_nop
+ {\secondargumentfalse
+ \thirdargumentfalse
+ \fourthargumentfalse
+ \fifthargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_quintuple_empty_two_spaced
+ \else
+ \expandafter\syst_helpers_quintuple_empty_two_normal
+ \fi}
+
+\def\syst_helpers_quintuple_empty_three_nop
+ {\thirdargumentfalse
+ \fourthargumentfalse
+ \fifthargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_quintuple_empty_three_spaced
+ \else
+ \expandafter\syst_helpers_quintuple_empty_three_normal
+ \fi}
+
+\def\syst_helpers_quintuple_empty_four_nop
+ {\fourthargumentfalse
+ \fifthargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_quintuple_empty_four_spaced
+ \else
+ \expandafter\syst_helpers_quintuple_empty_four_normal
+ \fi}
+
+\def\syst_helpers_quintuple_empty_five_nop
+ {\fifthargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_quintuple_empty_five_spaced
+ \else
+ \expandafter\syst_helpers_quintuple_empty_five_normal
+ \fi}
+
+\def\syst_helpers_quintuple_empty_two_spaced #1#2{#1[{#2}][][][][] }
+\def\syst_helpers_quintuple_empty_two_normal #1#2{#1[{#2}][][][][]}
+\def\syst_helpers_quintuple_empty_three_spaced #1#2#3{#1[{#2}][{#3}][][][] }
+\def\syst_helpers_quintuple_empty_three_normal #1#2#3{#1[{#2}][{#3}][][][]}
+\def\syst_helpers_quintuple_empty_four_spaced #1#2#3#4{#1[{#2}][{#3}][{#4}][][] }
+\def\syst_helpers_quintuple_empty_four_normal #1#2#3#4{#1[{#2}][{#3}][{#4}][][]}
+\def\syst_helpers_quintuple_empty_five_spaced #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][] }
+\def\syst_helpers_quintuple_empty_five_normal #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][]}
+
+%D Six
+
+\unexpanded\def\dosixtupleempty#1%
+ {\syst_helpers_argument_reset
+ \doifelsenextoptional
+ {\syst_helpers_sixtuple_empty_one_yes#1}
+ {\syst_helpers_sixtuple_empty_one_nop#1}}
+
+\def\syst_helpers_sixtuple_empty_one_yes#1[#2]%
+ {\firstargumenttrue
+ \doifelsenextoptional
+ {\syst_helpers_sixtuple_empty_two_yes#1{#2}}%
+ {\syst_helpers_sixtuple_empty_two_nop#1{#2}}}
+
+\def\syst_helpers_sixtuple_empty_two_yes#1#2[#3]%
+ {\secondargumenttrue
+ \doifelsenextoptional
+ {\syst_helpers_sixtuple_empty_three_yes#1{#2}{#3}}%
+ {\syst_helpers_sixtuple_empty_three_nop#1{#2}{#3}}}
+
+\def\syst_helpers_sixtuple_empty_three_yes#1#2#3[#4]%
+ {\thirdargumenttrue
+ \doifelsenextoptional
+ {\syst_helpers_sixtuple_empty_four_yes#1{#2}{#3}{#4}}%
+ {\syst_helpers_sixtuple_empty_four_nop#1{#2}{#3}{#4}}}
+
+\def\syst_helpers_sixtuple_empty_four_yes#1#2#3#4[#5]%
+ {\fourthargumenttrue
+ \doifelsenextoptional
+ {\syst_helpers_sixtuple_empty_five_yes#1{#2}{#3}{#4}{#5}}%
+ {\syst_helpers_sixtuple_empty_five_nop#1{#2}{#3}{#4}{#5}}}
+
+\def\syst_helpers_sixtuple_empty_five_yes#1#2#3#4#5[#6]%
+ {\fifthargumenttrue
+ \doifelsenextoptional
+ {\sixthargumenttrue#1[{#2}][{#3}][{#4}][{#5}][{#6}]}%
+ {\syst_helpers_sixtuple_empty_six_nop#1{#2}{#3}{#4}{#5}{#6}}}
+
+\def\syst_helpers_sixtuple_empty_one_nop#1%
+ {\firstargumentfalse
+ \secondargumentfalse
+ \thirdargumentfalse
+ \fourthargumentfalse
+ \fifthargumentfalse
+ \sixthargumentfalse
+ #1[][][][][][]}
+
+\def\syst_helpers_sixtuple_empty_two_nop
+ {\secondargumentfalse
+ \thirdargumentfalse
+ \fourthargumentfalse
+ \fifthargumentfalse
+ \sixthargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_sixtuple_empty_two_spaced
+ \else
+ \expandafter\syst_helpers_sixtuple_empty_two_normal
+ \fi}
+
+\def\syst_helpers_sixtuple_empty_three_nop
+ {\thirdargumentfalse
+ \fourthargumentfalse
+ \fifthargumentfalse
+ \sixthargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_sixtuple_empty_three_spaced
+ \else
+ \expandafter\syst_helpers_sixtuple_empty_three_normal
+ \fi}
+
+\def\syst_helpers_sixtuple_empty_four_nop
+ {\fourthargumentfalse
+ \fifthargumentfalse
+ \sixthargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_sixtuple_empty_four_spaced
+ \else
+ \expandafter\syst_helpers_sixtuple_empty_four_normal
+ \fi}
+
+\def\syst_helpers_sixtuple_empty_five_nop
+ {\fifthargumentfalse
+ \sixthargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_sixtuple_empty_five_spaced
+ \else
+ \expandafter\syst_helpers_sixtuple_empty_five_normal
+ \fi}
+
+\def\syst_helpers_sixtuple_empty_six_nop
+ {\sixthargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_sixtuple_empty_six_spaced
+ \else
+ \expandafter\syst_helpers_sixtuple_empty_six_normal
+ \fi}
+
+\def\syst_helpers_sixtuple_empty_two_spaced #1#2{#1[{#2}][][][][][] }
+\def\syst_helpers_sixtuple_empty_two_normal #1#2{#1[{#2}][][][][][]}
+\def\syst_helpers_sixtuple_empty_three_spaced #1#2#3{#1[{#2}][{#3}][][][][] }
+\def\syst_helpers_sixtuple_empty_three_normal #1#2#3{#1[{#2}][{#3}][][][][]}
+\def\syst_helpers_sixtuple_empty_four_spaced #1#2#3#4{#1[{#2}][{#3}][{#4}][][][] }
+\def\syst_helpers_sixtuple_empty_four_normal #1#2#3#4{#1[{#2}][{#3}][{#4}][][][]}
+\def\syst_helpers_sixtuple_empty_five_spaced #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][][] }
+\def\syst_helpers_sixtuple_empty_five_normal #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][][]}
+\def\syst_helpers_sixtuple_empty_six_spaced #1#2#3#4#5#6{#1[{#2}][{#3}][{#4}][{#5}][{#6}][] }
+\def\syst_helpers_sixtuple_empty_six_normal #1#2#3#4#5#6{#1[{#2}][{#3}][{#4}][{#5}][{#6}][]}
+
+%D Seven:
+
+\unexpanded\def\doseventupleempty#1%
+ {\syst_helpers_argument_reset
+ \doifelsenextoptional
+ {\syst_helpers_seventuple_empty_one_yes#1}%
+ {\syst_helpers_seventuple_empty_one_nop#1}}
+
+\def\syst_helpers_seventuple_empty_one_yes#1[#2]%
+ {\firstargumenttrue
+ \doifelsenextoptional
+ {\syst_helpers_seventuple_empty_two_yes#1{#2}}%
+ {\syst_helpers_seventuple_empty_two_nop#1{#2}}}
+
+\def\syst_helpers_seventuple_empty_two_yes#1#2[#3]%
+ {\secondargumenttrue
+ \doifelsenextoptional
+ {\syst_helpers_seventuple_empty_three_yes#1{#2}{#3}}%
+ {\syst_helpers_seventuple_empty_three_nop#1{#2}{#3}}}
+
+\def\syst_helpers_seventuple_empty_three_yes#1#2#3[#4]%
+ {\thirdargumenttrue
+ \doifelsenextoptional
+ {\syst_helpers_seventuple_empty_four_yes#1{#2}{#3}{#4}}%
+ {\syst_helpers_seventuple_empty_four_nop#1{#2}{#3}{#4}}}
+
+\def\syst_helpers_seventuple_empty_four_yes#1#2#3#4[#5]%
+ {\fourthargumenttrue
+ \doifelsenextoptional
+ {\syst_helpers_seventuple_empty_five_yes#1{#2}{#3}{#4}{#5}}%
+ {\syst_helpers_seventuple_empty_five_nop#1{#2}{#3}{#4}{#5}}}
+
+\def\syst_helpers_seventuple_empty_five_yes#1#2#3#4#5[#6]%
+ {\fifthargumenttrue
+ \doifelsenextoptional
+ {\syst_helpers_seventuple_empty_six_yes#1{#2}{#3}{#4}{#5}{#6}}%
+ {\syst_helpers_seventuple_empty_six_nop#1{#2}{#3}{#4}{#5}{#6}}}
+
+\def\syst_helpers_seventuple_empty_six_yes#1#2#3#4#5#6[#7]%
+ {\sixthargumenttrue
+ \doifelsenextoptional
+ {\seventhargumenttrue#1[{#2}][{#3}][{#4}][{#5}][{#6}][{#7}]}%
+ {\syst_helpers_seventuple_empty_seven_nop#1{#2}{#3}{#4}{#5}{#6}{#7}}}
+
+\def\syst_helpers_seventuple_empty_one_nop#1%
+ {\firstargumentfalse
+ \secondargumentfalse
+ \thirdargumentfalse
+ \fourthargumentfalse
+ \fifthargumentfalse
+ \sixthargumentfalse
+ \seventhargumentfalse
+ #1[][][][][][][]}
+
+\def\syst_helpers_seventuple_empty_two_nop
+ {\secondargumentfalse
+ \thirdargumentfalse
+ \fourthargumentfalse
+ \fifthargumentfalse
+ \sixthargumentfalse
+ \seventhargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_seventuple_empty_two_spaced
+ \else
+ \expandafter\syst_helpers_seventuple_empty_two_normal
+ \fi}
+
+\def\syst_helpers_seventuple_empty_three_nop
+ {\thirdargumentfalse
+ \fourthargumentfalse
+ \fifthargumentfalse
+ \sixthargumentfalse
+ \seventhargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_seventuple_empty_three_spaced
+ \else
+ \expandafter\syst_helpers_seventuple_empty_three_normal
+ \fi}
+
+\def\syst_helpers_seventuple_empty_four_nop
+ {\fourthargumentfalse
+ \fifthargumentfalse
+ \sixthargumentfalse
+ \seventhargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_seventuple_empty_four_spaced
+ \else
+ \expandafter\syst_helpers_seventuple_empty_four_normal
+ \fi}
+
+\def\syst_helpers_seventuple_empty_five_nop
+ {\fifthargumentfalse
+ \sixthargumentfalse
+ \seventhargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_seventuple_empty_five_spaced
+ \else
+ \expandafter\syst_helpers_seventuple_empty_five_normal
+ \fi}
+
+\def\syst_helpers_seventuple_empty_six_nop
+ {\sixthargumentfalse
+ \seventhargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_seventuple_empty_six_spaced
+ \else
+ \expandafter\syst_helpers_seventuple_empty_six_normal
+ \fi}
+
+\def\syst_helpers_seventuple_empty_seven_nop
+ {\seventhargumentfalse
+ \if_next_blank_space_token
+ \expandafter\syst_helpers_seventuple_empty_seven_spaced
+ \else
+ \expandafter\syst_helpers_seventuple_empty_seven_normal
+ \fi}
+
+\def\syst_helpers_seventuple_empty_two_spaced #1#2{#1[{#2}][][][][][][] }
+\def\syst_helpers_seventuple_empty_two_normal #1#2{#1[{#2}][][][][][][]}
+\def\syst_helpers_seventuple_empty_three_spaced #1#2#3{#1[{#2}][{#3}][][][][][] }
+\def\syst_helpers_seventuple_empty_three_normal #1#2#3{#1[{#2}][{#3}][][][][][]}
+\def\syst_helpers_seventuple_empty_four_spaced #1#2#3#4{#1[{#2}][{#3}][{#4}][][][][] }
+\def\syst_helpers_seventuple_empty_four_normal #1#2#3#4{#1[{#2}][{#3}][{#4}][][][][]}
+\def\syst_helpers_seventuple_empty_five_spaced #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][][][] }
+\def\syst_helpers_seventuple_empty_five_normal #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][][][]}
+\def\syst_helpers_seventuple_empty_six_spaced #1#2#3#4#5#6{#1[{#2}][{#3}][{#4}][{#5}][{#6}][][] }
+\def\syst_helpers_seventuple_empty_six_normal #1#2#3#4#5#6{#1[{#2}][{#3}][{#4}][{#5}][{#6}][][]}
+\def\syst_helpers_seventuple_empty_seven_spaced#1#2#3#4#5#6#7{#1[{#2}][{#3}][{#4}][{#5}][{#6}][{#7}][] }
+\def\syst_helpers_seventuple_empty_seven_normal#1#2#3#4#5#6#7{#1[{#2}][{#3}][{#4}][{#5}][{#6}][{#7}][]}
+
+\let\dosingleargument \dosingleempty
+\let\dodoubleargument \dodoubleempty
+\let\dotripleargument \dotripleempty
+\let\doquadrupleargument \doquadrupleempty
+\let\doquintupleargument \doquintupleempty
+\let\dosixtupleargument \dosixtupleempty
+\let\doseventupleargument\doseventupleempty
+
+%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}
+
+\let\strippedcsname\csstring
+
+%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.
+
+\unexpanded\def\complexorsimple#1%
+ {% \relax % prevents lookahead, brrr
+ \doifelsenextoptional
+ {\firstargumenttrue \csname\s!complex\csstring#1\endcsname}
+ {\firstargumentfalse\csname\s!simple \csstring#1\endcsname}}
+
+\unexpanded\def\complexorsimpleempty#1%
+ {% \relax % prevents lookahead, brrr
+ \doifelsenextoptional
+ {\firstargumenttrue \csname\s!complex\csstring#1\endcsname}
+ {\firstargumentfalse\csname\s!complex\csstring#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.
+
+\unexpanded\def\syst_helpers_complex_or_simple#1#2%
+ {\doifelsenextoptional{\firstargumenttrue#1}{\firstargumentfalse#2}}
+
+\unexpanded\def\syst_helpers_complex_or_simple_empty#1%
+ {\doifelsenextoptional{\firstargumenttrue#1}{\firstargumentfalse#1[]}}
+
+\unexpanded\def\definecomplexorsimple#1%
+ {\unexpanded\edef#1{\syst_helpers_complex_or_simple
+ \expandafter\noexpand\csname\s!complex\csstring#1\endcsname
+ \expandafter\noexpand\csname\s!simple \csstring#1\endcsname}}
+
+\unexpanded\def\definecomplexorsimpleempty#1%
+ {\unexpanded\edef#1{\syst_helpers_complex_or_simple_empty
+ \expandafter\noexpand\csname\s!complex\csstring#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 {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 We can add additional definitions later when we have defined
+%D \type {\appendtoks}.
+
+\newconditional\c_syst_helpers_permit_spaces_between_groups
+
+\unexpanded\def \permitspacesbetweengroups{\settrue \c_syst_helpers_permit_spaces_between_groups}
+\unexpanded\def\dontpermitspacesbetweengroups{\setfalse\c_syst_helpers_permit_spaces_between_groups}
+
+\dontpermitspacesbetweengroups
+
+%D We can avoid the nasty if handling in \type {syst-gen} by splitting
+%D the lot in pieces so that we have no nested \type {\nextarguments}
+%D potentially being an \type {conditional} token. Okay, these macros
+%D are not called that often but it saves crap when tracing.
+
+\unexpanded\def\syst_helpers_get_grouped_argument#1#2%
+ {\let\syst_helpers_get_grouped_argument_yes#1%
+ \let\syst_helpers_get_grouped_argument_nop#2%
+ \futurelet\nextargument\syst_helpers_get_grouped_argument_indeed}
+
+\def\syst_helpers_get_grouped_argument_indeed
+ {\ifx\nextargument\bgroup
+ \expandafter\syst_helpers_get_grouped_argument_a
+ \else
+ \expandafter\syst_helpers_get_grouped_argument_b
+ \fi}
+
+\def\syst_helpers_get_grouped_argument_a
+ {\syst_helpers_argument_reset
+ \syst_helpers_get_grouped_argument_yes\syst_helpers_get_grouped_argument_nested}
+
+\def\syst_helpers_get_grouped_argument_b
+ {\ifconditional\c_syst_helpers_permit_spaces_between_groups
+ \expandafter\syst_helpers_get_grouped_argument_f
+ \else
+ \expandafter\syst_helpers_get_grouped_argument_d
+ \fi}
+
+\def\syst_helpers_get_grouped_argument_d
+ {\syst_helpers_argument_error
+ \syst_helpers_get_grouped_argument_nop\syst_helpers_get_grouped_argument_nested{}}
+
+\begingroup
+ \def\\ {\syst_helpers_get_grouped_argument\syst_helpers_get_grouped_argument_yes\syst_helpers_get_grouped_argument_nop}
+ \global\let\syst_helpers_get_grouped_argument_e\\
+\endgroup
+
+\def\syst_helpers_get_grouped_argument_f
+ {\ifx\nextargument\blankspace
+ \expandafter\syst_helpers_get_grouped_argument_e % g
+ \else
+ \expandafter\syst_helpers_get_grouped_argument_d % h
+ \fi}
+
+\def\dosinglegroupempty#1%
+ {\def\syst_helpers_get_grouped_argument_nested
+ {\dontpermitspacesbetweengroups
+ #1}%
+ \syst_helpers_get_grouped_argument\firstargumenttrue\firstargumentfalse}
+
+\def\dodoublegroupempty#1%
+ {\def\syst_helpers_get_grouped_argument_nested##1%
+ {\def\syst_helpers_get_grouped_argument_nested
+ {\dontpermitspacesbetweengroups
+ #1{##1}}%
+ \syst_helpers_get_grouped_argument\secondargumenttrue\secondargumentfalse}%
+ \syst_helpers_get_grouped_argument\firstargumenttrue\firstargumentfalse}
+
+\def\dotriplegroupempty#1%
+ {\def\syst_helpers_get_grouped_argument_nested##1%
+ {\def\syst_helpers_get_grouped_argument_nested####1%
+ {\def\syst_helpers_get_grouped_argument_nested
+ {\dontpermitspacesbetweengroups
+ #1{##1}{####1}}%
+ \syst_helpers_get_grouped_argument\thirdargumenttrue\thirdargumentfalse}%
+ \syst_helpers_get_grouped_argument\secondargumenttrue\secondargumentfalse}%
+ \syst_helpers_get_grouped_argument\firstargumenttrue\firstargumentfalse}
+
+\def\doquadruplegroupempty#1%
+ {\def\syst_helpers_get_grouped_argument_nested##1%
+ {\def\syst_helpers_get_grouped_argument_nested####1%
+ {\def\syst_helpers_get_grouped_argument_nested########1%
+ {\def\syst_helpers_get_grouped_argument_nested
+ {\dontpermitspacesbetweengroups
+ #1{##1}{####1}{########1}}%
+ \syst_helpers_get_grouped_argument\fourthargumenttrue\fourthargumentfalse}%
+ \syst_helpers_get_grouped_argument\thirdargumenttrue\thirdargumentfalse}%
+ \syst_helpers_get_grouped_argument\secondargumenttrue\secondargumentfalse}%
+ \syst_helpers_get_grouped_argument\firstargumenttrue\firstargumentfalse}
+
+\def\doquintuplegroupempty#1%
+ {\def\syst_helpers_get_grouped_argument_nested##1%
+ {\def\syst_helpers_get_grouped_argument_nested####1%
+ {\def\syst_helpers_get_grouped_argument_nested########1%
+ {\def\syst_helpers_get_grouped_argument_nested################1%
+ {\def\syst_helpers_get_grouped_argument_nested
+ {\dontpermitspacesbetweengroups
+ #1{##1}{####1}{########1}{################1}}%
+ \syst_helpers_get_grouped_argument\fifthargumenttrue\fifthargumentfalse}%
+ \syst_helpers_get_grouped_argument\fourthargumenttrue\fourthargumentfalse}%
+ \syst_helpers_get_grouped_argument\thirdargumenttrue\thirdargumentfalse}%
+ \syst_helpers_get_grouped_argument\secondargumenttrue\secondargumentfalse}%
+ \syst_helpers_get_grouped_argument\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.
+
+\def\firstofoneargument #1{#1}
+
+\def\firstoftwoarguments #1#2{#1}
+\def\secondoftwoarguments #1#2{#2}
+
+\def\firstofthreearguments #1#2#3{#1}
+\def\secondofthreearguments #1#2#3{#2}
+\def\thirdofthreearguments #1#2#3{#3}
+
+\def\firstoffourarguments #1#2#3#4{#1}
+\def\secondoffourarguments #1#2#3#4{#2}
+\def\thirdoffourarguments #1#2#3#4{#3}
+\def\fourthoffourarguments #1#2#3#4{#4}
+
+\def\firstoffivearguments #1#2#3#4#5{#1}
+\def\secondoffivearguments #1#2#3#4#5{#2}
+\def\thirdoffivearguments #1#2#3#4#5{#3}
+\def\fourthoffivearguments #1#2#3#4#5{#4}
+\def\fifthoffivearguments #1#2#3#4#5{#5}
+
+\def\firstofsixarguments #1#2#3#4#5#6{#1}
+\def\secondofsixarguments#1#2#3#4#5#6{#2}
+\def\thirdofsixarguments #1#2#3#4#5#6{#3}
+\def\fourthofsixarguments#1#2#3#4#5#6{#4}
+\def\fifthofsixarguments #1#2#3#4#5#6{#5}
+\def\sixthofsixarguments #1#2#3#4#5#6{#6}
+
+\unexpanded\def\firstofoneunexpanded #1{#1}
+
+\unexpanded\def\firstoftwounexpanded #1#2{#1}
+\unexpanded\def\secondoftwounexpanded #1#2{#2}
+
+\unexpanded\def\firstofthreeunexpanded #1#2#3{#1}
+\unexpanded\def\secondofthreeunexpanded#1#2#3{#2}
+\unexpanded\def\thirdofthreeunexpanded #1#2#3{#3}
+
+%D \macros
+%D {globalletempty,letempty,
+%D letvalueempty,letgvalueempty,
+%D letvaluerelax,letgvaluerelax}
+%D
+%D Trivial:
+
+\unexpanded\def\letempty #1{\let#1\empty}
+\unexpanded\def\globalletempty#1{\global\let#1\empty}
+
+\unexpanded\def\letvalueempty #1{\expandafter\let\csname#1\endcsname\empty}
+\unexpanded\def\letgvalueempty#1{\global\expandafter\let\csname#1\endcsname\empty}
+\unexpanded\def\letvaluerelax #1{\expandafter\let\csname#1\endcsname\relax}
+\unexpanded\def\letgvalurelax #1{\global\expandafter\let\csname#1\endcsname\relax}
+
+\unexpanded\def\relaxvalueifundefined#1%
+ {\ifcsname#1\endcsname \else
+ \expandafter\let\csname#1\endcsname\relax
+ \fi}
+
+%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.
+
+\unexpanded\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}.
+
+\setnewconstant\statuswidth 15
+\setnewconstant\statuswrite 128 % \pluscxxviii
+
+\ifdefined\writestring \else
+
+ \newtoks\everywritestring
+
+ \def\writedirect {\immediate\write\statuswrite}
+ \def\writeline {\writedirect{}}
+ \unexpanded\def\writestring#1{\begingroup\the\everywritestring\writedirect{#1}\endgroup}
+
+\fi
+
+\unexpanded\def\normalwritestatus#1#2%
+ {\writestring{\expandafter\syst_helpers_split_status_yes\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\syst_helpers_split_status_yes#1#2%
+ {\ifcase#1 \expandafter\syst_helpers_split_status_nop\fi#2%
+ \expandafter\syst_helpers_split_status_yes\expandafter{\the\numexpr#1+\minusone\relax}}
+
+\def\syst_helpers_split_status_nop#1\end
+ {}
+
+%D \macros
+%D {immediatemessage}
+%D
+%D A fully expandable message:
+
+\let\immediatemessage\clf_immediatemessage % {} mandate
+
+%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
+
+\unexpanded\def\debuggerinfo#1#2%
+ {\ifdebuggerinfo
+ \writestatus{debugger}{#1:: #2}%
+ \fi}
+
+\ifdefined\writestatus \else \let\writestatus\normalwritestatus \fi
+\ifdefined\writebanner \else \unexpanded\def\writebanner{\writestring} \fi
+
+% % % % % % % % % % % % % % % % % % % % % % % %
+
+%D \macros
+%D {rawgetparameters}
+%D
+%D A raw and dirty alternative for \type {\getparameters}; no
+%D checking is done!
+
+\unexpanded\def\rawsetparameter#1=#2,%
+ {\if]#1\else
+ \expandafter\def\csname\rawparameterprefix#1\endcsname{#2}%
+ \expandafter\rawsetparameter
+ \fi}
+
+\unexpanded\def\rawgetparameters[#1][#2% some 5-10% faster
+ {\ifx#2]% test is needed, else bomb on [#1][]
+ \expandafter\gobbleoneargument
+ \else
+ \def\rawparameterprefix{#1}%
+ \expandafter\dorawgetparameters
+ \fi#2}
+
+\def\dorawgetparameters#1]%
+ {\expandafter\rawsetparameter#1,]=,}
+
+%D \macros
+%D {doglobal,
+%D redoglobal,dodoglobal,resetglobal}
+%D
+%D The two macros \type {\redoglobal} and \type{\dodoglobal} are
+%D used in this and some other modules to enforce a user
+%D specified \type {\doglobal} action. The last and often only
+%D global assignment in a macro is done with
+%D \type {\dodoglobal}, but all preceding ones with
+%D \type {\redoglobal}. When using only alternatives, one can
+%D reset this mechanism with \type {\resetglobal}.
+
+\unexpanded\def\resetglobal
+ {\let\redoglobal\relax
+ \let\dodoglobal\relax}
+
+\resetglobal
+
+\unexpanded\def\doglobal
+ {\ifx\redoglobal\relax
+ \let\redoglobal\global
+ \let\dodoglobal\syst_helpers_dodo_global
+ \fi}
+
+\def\syst_helpers_dodo_global
+ {\resetglobal\global}
+
+\def\saveglobal
+ {\let\syst_helpers_dodo_global\dodoglobal
+ \let\syst_helpers_redo_global\redoglobal}
+
+\def\restoreglobal
+ {\let\redoglobal\syst_helpers_redo_global
+ \let\dodoglobal\syst_helpers_dodo_global}
+
+%D A very useful application of this macro is \type {\newif},
+%D \TEX's fake boolean type. Not being a primitive,
+%D \type {\global} hopelessly fails here. But a slight
+%D adaption of Knuth's original macro permits:
+%D
+%D \starttyping
+%D \doglobal\newif\iftest
+%D \stoptyping
+%D
+%D Of course one can still say:
+%D
+%D \starttyping
+%D \global\testtrue
+%D \global\testfalse
+%D \stoptyping
+%D
+%D Apart from the prefixes, a few more \type{\expandafters}
+%D are needed:
+
+\unexpanded\def\newif#1%
+ {\scratchcounter\escapechar
+ \escapechar\minusone
+ \expandafter\expandafter\expandafter
+ \redoglobal\expandafter\expandafter\expandafter
+ \edef\@if#1{true}{\let\noexpand#1\noexpand\iftrue}%
+ \expandafter\expandafter\expandafter
+ \redoglobal\expandafter\expandafter\expandafter
+ \edef\@if#1{false}{\let\noexpand#1\noexpand\iffalse}%
+ \dodoglobal\@if#1{false}%
+ \escapechar\scratchcounter}
+
+%D Also new:
+
+\unexpanded\def\define#1%
+ {\ifdefined#1%
+ \message{[\noexpand#1is already defined]}%
+ \unexpanded\expandafter\def\expandafter\gobbleddefinition
+ \else
+ \unexpanded\expandafter\def
+ \fi#1}
+
+\unexpanded\def\redefine#1%
+ {\ifdefined#1%
+ \message{[\noexpand#1is redefined]}%
+ \fi
+ \unexpanded\def#1}
+
+\unexpanded\def\definemacro#1%
+ {\ifdefined#1%
+ \message{[\noexpand#1is already defined]}%
+ \unexpanded\expandafter\def\expandafter\gobbleddefinition
+ \else
+ \unexpanded\expandafter\def
+ \fi#1}
+
+% \define\hans{hans}
+% \redefine\hans{hans}
+% \define\hans#1[]#2#3{hans}
+
+%D The next variant fits nicely in the setups syntax:
+%D
+%D \starttyping
+%D \starttexdefinition bagger [#1] #2
+%D oeps
+%D #1
+%D oeps
+%D \stoptexdefinition
+%D
+%D \bagger [a] {b}
+%D \stoptyping
+
+% \starttexdefinition test
+% oeps
+% \stoptexdefinition
+%
+% [\test]
+
+\def\s!unexpanded{unexpanded}
+
+% \bgroup \obeylines
+%
+% \global\let\stoptexdefinition\relax
+%
+% \unexpanded\gdef\starttexdefinition%
+% {\bgroup%
+% \obeylines%
+% \syst_helpers_start_tex_definition}
+%
+% \gdef\syst_helpers_start_tex_definition #1
+% {\catcode\endoflineasciicode\ignorecatcode%
+% \doifinstringelse\letterhash{\detokenize{#1}}\syst_helpers_start_tex_definition_yes\syst_helpers_start_tex_definition_nop#1
+% }
+%
+% \gdef\syst_helpers_start_tex_definition_yes#1 #2
+% {\edef\texdefinitionname{#1}%
+% \ifx\texdefinitionname\s!unexpanded%
+% \expandafter\syst_helpers_start_tex_definition_yes_unexpanded%
+% \else%
+% \expandafter\syst_helpers_start_tex_definition_yes_normal%
+% \fi%
+% {#1}#2
+% }
+%
+% \gdef\syst_helpers_start_tex_definition_yes_unexpanded#1#2 #3
+% #4\stoptexdefinition%
+% {\egroup% #1=unexpanded
+% \unexpanded\expandafter\def\csname#2\endcsname#3{#4}}
+%
+% \gdef\syst_helpers_start_tex_definition_yes_normal#1#2
+% #3\stoptexdefinition%
+% {\egroup%
+% \expandafter\def\csname#1\endcsname#2{#3}}
+%
+% \gdef\syst_helpers_start_tex_definition_nop#1
+% {\syst_helpers_start_tex_definition_nop_indeed{#1}{}}
+%
+% \gdef\syst_helpers_start_tex_definition_nop_indeed#1#2#3\stoptexdefinition%
+% {\egroup%
+% \expandafter\def\csname#1\endcsname{#3}}
+%
+% \egroup
+
+% \starttexdefinition unexpanded test #1
+% [here #1]
+% \stoptexdefinition
+%
+% \starttexdefinition global unexpanded test
+% [here test]
+% \stoptexdefinition
+%
+% \scratchcounter=123
+%
+% \starttexdefinition global unexpanded expanded test #oeps
+% [here #oeps: \the\scratchcounter]
+% \stoptexdefinition
+
+% \bgroup \obeylines
+%
+% \global\let\stoptexdefinition\relax
+%
+% \unexpanded\gdef\starttexdefinition%
+% {\bgroup%
+% \obeylines%
+% \syst_helpers_start_tex_definition_one}
+%
+% \gdef\syst_helpers_start_tex_definition_one#1
+% {\catcode\endoflineasciicode\ignorecatcode%
+% \syst_helpers_start_tex_definition_two{#1}}
+%
+% \gdef\syst_helpers_start_tex_definition_two#1#2\stoptexdefinition%
+% {\egroup%
+% \ctxcommand{thetexdefinition("#1")}{#2}}
+%
+% \egroup
+
+\bgroup \obeylines
+
+\global\let\stoptexdefinition\relax
+
+\unexpanded\gdef\starttexdefinition%
+ {\bgroup%
+ \obeylines%
+ \syst_helpers_start_tex_definition}
+
+\gdef\syst_helpers_start_tex_definition#1
+ {\catcode\endoflineasciicode\ignorecatcode%
+ \clf_texdefinition_one{#1}}
+
+\gdef\dostarttexdefinition#1\stoptexdefinition%
+ {\egroup%
+ \clf_texdefinition_two{#1}}
+
+\egroup
+
+% \unexpanded\def\texdefinition#1{\csname\ifcsname#1\endcsname#1\else donothing\fi\endcsname} % todo: a nop cs: char 0 or some corenamespace
+
+\unexpanded\def\texdefinition#1{\begincsname#1\endcsname}
+
+% This is a first variant, more might be added:
+
+\unexpanded\def\starttexcode{\unprotect}
+\unexpanded\def\stoptexcode {\protect}
+
+%D \macros
+%D {newcounter,
+%D increment,decrement}
+%D
+%D Unfortunately the number of \COUNTERS\ in \TEX\ is limited,
+%D but fortunately we can store numbers in a macro. We can
+%D increment such pseudo \COUNTERS\ with \type{\increment}.
+%D
+%D \starttyping
+%D \increment(\counter,20)
+%D \increment(\counter,-4)
+%D \increment(\counter)
+%D \increment\counter
+%D \stoptyping
+%D
+%D After this sequence of commands, the value of
+%D \type{\counter} is 20, 16, 17 and~18. Of course there is
+%D also the complementary command \type{\decrement}.
+%D
+%D Global assignments are possible too, using \type{\doglobal}:
+%D
+%D \starttyping
+%D \doglobal\increment\counter
+%D \stoptyping
+%D
+%D When \type{\counter} is undefined, it's value is initialized
+%D at~0. It is nevertheless better to define a \COUNTER\
+%D explicitly. One reason could be that the \COUNTER\ can be
+%D part of a test with \type{\ifnum} and this conditional does
+%D not accept undefined macro's. The \COUNTER\ in our example
+%D can for instance be defined with:
+%D
+%D \starttyping
+%D \newcounter\counter
+%D \stoptyping
+%D
+%D The command \type{\newcounter} must not be confused with
+%D \type{\newcount}! Of course this mechanism is much slower
+%D than using \TEX's \COUNTERS\ directly. In practice
+%D \COUNTERS\ (and therefore our pseudo counters too) are
+%D seldom the bottleneck in the processing of a text. Apart
+%D from some other incompatilities we want to mention a pitfal
+%D when using \type{\ifnum}.
+%D
+%D \starttyping
+%D \ifnum\normalcounter=\pseudocounter \doif \else \doelse \fi
+%D \ifnum\pseudocounter=\normalcounter \doif \else \doelse \fi
+%D \stoptyping
+%D
+%D In the first test, \TEX\ continues it's search for the
+%D second number after reading \type{\pseudocounter}, while
+%D in the second test, it stops reading after having
+%D encountered a real one. Tests like the first one therefore
+%D can give unexpected results, for instance execution
+%D of \type{\doif} even if both numbers are unequal.
+
+\def\zerocountervalue{0}
+
+\unexpanded\def\newcounter#1%
+ {\dodoglobal\let#1\zerocountervalue}
+
+%D Nowadays we don't mind a few more tokens if we can gain a
+%D bit of speed.
+
+\def\syst_helpers_do_increment#1{\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi+\plusone \relax}}
+\def\syst_helpers_do_decrement#1{\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi+\minusone\relax}}
+
+\def\syst_helpers_do_do_do_increment#1,#2){\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi+#2\relax}}
+\def\syst_helpers_do_do_do_decrement#1,#2){\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi-#2\relax}}
+
+\def\syst_helpers_do_do_increment(#1{\doifelsenextchar,{\syst_helpers_do_do_do_increment#1}{\syst_helpers_do_do_do_increment#1,\plusone}}
+\def\syst_helpers_do_do_decrement(#1{\doifelsenextchar,{\syst_helpers_do_do_do_decrement#1}{\syst_helpers_do_do_do_decrement#1,\plusone}}
+
+\unexpanded\def\fastincrement#1{\dodoglobal\edef#1{\the\numexpr#1+\plusone \relax}}
+\unexpanded\def\fastdecrement#1{\dodoglobal\edef#1{\the\numexpr#1+\minusone\relax}}
+
+\unexpanded\def\increment{\doifelsenextchar(\syst_helpers_do_do_increment\syst_helpers_do_increment}
+\unexpanded\def\decrement{\doifelsenextchar(\syst_helpers_do_do_decrement\syst_helpers_do_decrement}
+
+\unexpanded\def\incrementvalue#1{\expandafter\increment\csname#1\endcsname}
+\unexpanded\def\decrementvalue#1{\expandafter\decrement\csname#1\endcsname}
+
+%D \macros
+%D {newsignal}
+%D
+%D When writing advanced macros, we cannot do without
+%D signaling. A signal is a small (invisible) kern or penalty
+%D that signals the next macro that something just happened.
+%D This macro can take any action depending on the previous
+%D signal. Signals must be unique and the next macro takes care
+%D of that.
+%D
+%D \starttyping
+%D \newsignal\somesignal
+%D \stoptyping
+%D
+%D Signals old dimensions and can be used in skips, kerns and
+%D tests like \type{\ifdim}.
+
+\newdimen\maximumsignal % step is about 0.00025pt
+
+\unexpanded\def\newsignal#1%
+ {\ifdefined#1\else
+ \advance\maximumsignal 2\scaledpoint % to be save in rounding
+ \edef#1{\the\maximumsignal}%
+ \fi}
+
+%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
+
+% \def\checkedstrippedcsname#1% this permits \strippedcsname{\xxx} and \strippedcsname{xxx}
+% {\expandafter\syst_helpers_checked_stripped_csname\string#1}
+%
+% \def\syst_helpers_checked_stripped_csname#1%
+% %{\ifx#1\letterbackslash\else#1\fi}
+% {\if\noexpand#1\letterbackslash\else#1\fi}
+
+\let\checkedstrippedcsname\csstring
+
+%D \macros
+%D {savenormalmeaning}
+%D
+%D We will use this one in:
+
+\unexpanded\def\savenormalmeaning#1%
+ {\ifcsname normal\csstring#1\endcsname \else
+ \expandafter\let\csname normal\csstring#1\endcsname#1%
+ \fi}
+
+%D \macros
+%D {dorecurse,recurselevel,recursedepth,
+%D dostepwiserecurse}
+%D
+%D \TEX\ does not offer us powerfull for||loop mechanisms. On
+%D the other hand its recursion engine is quite unique. We
+%D therefore identify the for||looping macros by this method.
+%D The most simple alternative is the one that only needs a
+%D number.
+%D
+%D \starttyping
+%D \dorecurse {n} {whatever we want}
+%D \stoptyping
+%D
+%D This macro can be nested without problems and therefore be
+%D used in situations where \PLAIN\ \TEX's \type{\loop} macro
+%D ungracefully fails. The current value of the counter is
+%D available in \type{\recurselevel}, before as well as after
+%D the \typ{whatever we wat} stuff.
+%D
+%D \starttyping
+%D \dorecurse % inner loop
+%D {10}
+%D {\recurselevel: % outer value
+%D \dorecurse % inner loop
+%D {\recurselevel} % outer value
+%D {\recurselevel} % inner value
+%D \dorecurse % inner loop
+%D {\recurselevel} % outer value
+%D {\recurselevel} % inner value
+%D \endgraf}
+%D \stoptyping
+%D
+%D In this example the first, second and fourth
+%D \type{\recurselevel} concern the outer loop, while the third
+%D and fifth one concern the inner loop. The depth of the
+%D nesting is available for inspection in \type{\recursedepth}.
+%D
+%D Both \type{\recurselevel} and \type{\recursedepth} are
+%D macros. The real \COUNTERS\ are hidden from the user because
+%D we don't want any interference.
+
+\newcount\outerrecurse
+\newcount\innerrecurse
+
+\def\recursedepth{\the\outerrecurse}
+\def\recurselevel{0}
+
+\let\syst_helpers_stepwise_next\relax
+
+\installsystemnamespace{recurseindex}
+\installsystemnamespace{recurseaction}
+
+\unexpanded\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4
+ {\global\advance\outerrecurse \plusone
+ \global\expandafter\def\csname\??recurseaction\recursedepth\endcsname{#4}%
+ \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel
+ \ifnum#3>\zerocount\relax
+ \ifnum#2<#1\relax
+ \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit
+ \else
+ \let\syst_helpers_stepwise_next\syst_helpers_stepwise_recurse
+ \fi
+ \else
+ \ifnum#3<\zerocount\relax
+ \ifnum#1<#2\relax
+ \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit
+ \else
+ \let\syst_helpers_stepwise_next\syst_helpers_stepwise_reverse
+ \fi
+ \else
+ \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit
+ \fi
+ \fi\normalexpanded{\syst_helpers_stepwise_next{\number#1}{\number#2}{\number#3}}}
+
+\unexpanded\def\syst_helpers_stepwise_recurse#1#2#3% from to step
+ {\ifnum#1>#2\relax
+ \expandafter\syst_helpers_stepwise_recurse_nop
+ \else
+ \def\recurselevel{#1}%
+ \doubleexpandafter\syst_helpers_stepwise_recurse_yes\expandafter
+ \fi\expandafter{\the\numexpr\recurselevel+#3\relax}{#2}{#3}}
+
+\unexpanded\def\syst_helpers_recurse_content
+ {\csname\??recurseaction\recursedepth\endcsname}
+
+\unexpanded\def\syst_helpers_stepwise_recurse_yes
+ {\syst_helpers_recurse_content
+ \syst_helpers_stepwise_recurse}
+
+\unexpanded\def\syst_helpers_stepwise_reverse#1#2#3% from to step
+ {\ifnum#1<#2\relax
+ \expandafter\syst_helpers_stepwise_recurse_nop
+ \else
+ \def\recurselevel{#1}%
+ \innerrecurse#1\relax
+ \advance\innerrecurse#3\relax
+ \doubleexpandafter\syst_helpers_stepwise_reverse_yes\expandafter
+ \fi\expandafter{\the\innerrecurse}{#2}{#3}}
+
+\unexpanded\def\syst_helpers_stepwise_reverse_yes
+ {\syst_helpers_recurse_content
+ \syst_helpers_stepwise_reverse}
+
+\unexpanded\def\syst_helpers_stepwise_exit
+ {\syst_helpers_stepwise_recurse_nop\relax}
+
+\unexpanded\def\syst_helpers_stepwise_recurse_nop#1#2#3#4%
+ {\expandafter\let\expandafter\recurselevel\csname\??recurseindex\recursedepth\endcsname
+ \global\advance\outerrecurse\minusone}
+
+% \unexpanded\def\nonostepwiserecurse#1#2#3%
+% {\expandafter\let\expandafter\recurselevel\csname\??recurseindex\recursedepth\endcsname
+% \global\advance\outerrecurse\minusone}
+
+\unexpanded\def\dorecurse#1%
+ {\dostepwiserecurse\plusone{#1}\plusone}
+
+\def\doexpandedrecurse#1#2% user macro (also was \doxprecurse)
+ {\ifnum#1>\zerocount
+ #2\expandafter\doexpandedrecurse\expandafter{\the\numexpr#1-\plusone\relax}{#2}%
+ \fi}
+
+%D As we can see here, the simple command \type{\dorecurse} is
+%D a special case of the more general:
+%D
+%D \starttyping
+%D \dostepwiserecurse {from} {to} {step} {action}
+%D \stoptyping
+%D
+%D This commands accepts positive and negative steps. Illegal
+%D values are handles as good as possible and the macro accepts
+%D numbers and \COUNTERS.
+%D
+%D \starttyping
+%D \dostepwiserecurse {1} {10} {2} {...}
+%D \dostepwiserecurse {10} {1} {-2} {...}
+%D \stoptyping
+%D
+%D Because the simple case is used often, we implement it
+%D more efficiently:
+
+\unexpanded\def\dorecurse#1%
+ {\ifcase#1\relax
+ \expandafter\gobbletwoarguments
+ \or
+ \expandafter\syst_helpers_recurse_y
+ \else
+ \expandafter\syst_helpers_recurse_x
+ \fi{#1}}
+
+\unexpanded\def\syst_helpers_recurse_x#1#2%
+ {\global\advance\outerrecurse \plusone
+ \expandafter\gdef\csname\??recurseaction\recursedepth\endcsname{#2}%
+ \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel
+ \expandafter\syst_helpers_recurse_indeed\expandafter1\expandafter{\number#1}}
+
+\unexpanded\def\syst_helpers_recurse_y#1#2%
+ {\global\advance\outerrecurse \plusone
+ \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel
+ \let\recurselevel\!!plusone
+ #2%
+ \expandafter\let\expandafter\recurselevel\csname\??recurseindex\recursedepth\endcsname
+ \global\advance\outerrecurse \minusone}
+
+\unexpanded\def\syst_helpers_recurse_indeed#1#2% from to
+ {\ifnum#1>#2\relax
+ \expandafter\syst_helpers_recurse_indeed_nop
+ \else
+ \def\recurselevel{#1}%
+ \doubleexpandafter\syst_helpers_recurse_indeed_yes
+ \fi\expandafter{\the\numexpr\recurselevel+\plusone\relax}{#2}}
+
+\unexpanded\def\syst_helpers_recurse_indeed#1#2% from to
+ {\ifnum#1>#2\relax
+ \expandafter\syst_helpers_recurse_indeed_nop
+ \else
+ \def\recurselevel{#1}%
+ \innerrecurse#1\advance\innerrecurse\plusone
+ \doubleexpandafter\syst_helpers_recurse_indeed_yes
+ \fi\expandafter{\the\innerrecurse}{#2}}
+
+\unexpanded\def\syst_helpers_recurse_indeed_yes
+ {\syst_helpers_recurse_content
+ \syst_helpers_recurse_indeed}
+
+\unexpanded\def\syst_helpers_recurse_indeed_nop#1#2#3%
+ {\expandafter\let\expandafter\recurselevel\csname\??recurseindex\recursedepth\endcsname
+ \global\advance\outerrecurse \minusone }
+
+%D \macros
+%D {dowith}
+%D
+%D Here's a loop over whatever is in a list:
+%D
+%D \starttyping
+%D \dowith{a,b,c}{[#1]}
+%D \stoptyping
+
+\unexpanded\def\dowith#1#2%
+ {\def\syst_helpers_with##1{#2}%
+ \normalexpanded{\processcommalist[#1]}\syst_helpers_with}
+
+%D \macros
+%D {doloop,exitloop}
+%D
+%D Sometimes loops are not determined by counters, but by
+%D (a combinations of) conditions. We therefore implement a
+%D straightforward loop, which can only be left when we
+%D explictly exit it. Nesting is supported. First we present
+%D a more extensive alternative.
+%D
+%D \starttyping
+%D \doloop
+%D {Some kind of typesetting punishment \par
+%D \ifnum\pageno>100 \exitloop \fi}
+%D \stoptyping
+%D
+%D When needed, one can call for \type{\looplevel} and
+%D \type{\loopdepth}.
+
+\let\endofloop\donothing % maybe \syst_helpers_loop_end
+
+\unexpanded\def\doloop#1%
+ {\global\advance\outerrecurse \plusone
+ \expandafter\gdef\csname\??recurseaction\recursedepth\endcsname{#1}%
+ \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel
+ \let\endofloop\syst_helpers_loop
+ \syst_helpers_loop1} % no \plusone else \recurselevel wrong
+
+\unexpanded\def\syst_helpers_loop#1%
+ {\def\recurselevel{#1}%
+ \expandafter\syst_helpers_loop_yes\expandafter{\the\numexpr\recurselevel+\plusone\relax}}
+
+\unexpanded\def\syst_helpers_loop_yes
+ {\syst_helpers_recurse_content
+ \endofloop}
+
+\unexpanded\def\syst_helpers_loop_nop#1%
+ {\let\endofloop\syst_helpers_loop % new, permits nested \doloop's
+ \expandafter\let\expandafter\recurselevel\csname\??recurseindex\recursedepth\endcsname
+ \global\advance\outerrecurse\minusone}
+
+\unexpanded\def\exitloop % \exitloop quits at end
+ {\let\endofloop\syst_helpers_loop_nop}
+
+\unexpanded\def\exitloopnow#1\endofloop % \exitloopnow quits directly
+ {\syst_helpers_loop_nop}
+
+%D The loop is executed at least once, so beware of situations
+%D like:
+%D
+%D \starttyping
+%D \doloop {\exitloop some commands}
+%D \stoptyping
+%D
+%D It's just a matter of putting the text into the \type{\if}
+%D statement that should be there anyway, like in:
+%D
+%D \starttyping
+%D \doloop {\ifwhatever \exitloop \else some commands\fi}
+%D \stoptyping
+%D
+%D You can also quit a loop immediately, by using \type
+%D {\exitloopnow} instead. Beware, this is more sensitive
+%D for conditional errors.
+
+%D Krzysztof Leszczynski suggested to provide access to the level by
+%D means of a \type {#1}. I decided to pass the more frequently used
+%D level as \type {#1} and the less favoured depth as \type {#2}. The
+%D intended usage is:
+%D
+%D \starttyping
+%D \dorecurse{3}{\definesymbol[test-#1][xx-#1]}
+%D
+%D \def\test{\dorecurse{3}{\definesymbol[test-##1][xx-##1]}} \test
+%D
+%D \symbol[test-1]\quad\symbol[test-2]\quad\symbol[test-3]
+%D \stoptyping
+%D
+%D Since the hashed arguments are expanded, we don't need tricky
+%D expansion here.
+%D
+%D \starttyping
+%D \dorecurse{3}{\expanded{\definesymbol[test-\recurselevel][xx-\recurselevel]}}
+%D \stoptyping
+
+\def\syst_helpers_recurse_content
+ {\csname\??recurseaction\recursedepth\expandafter\expandafter\expandafter\endcsname
+ \expandafter\expandafter\expandafter{\expandafter\recurselevel\expandafter}\expandafter{\recursedepth}}
+
+\unexpanded\def\syst_helpers_recurse_x#1#2%
+ {\global\advance\outerrecurse \plusone
+ \global\expandafter\def\csname\??recurseaction\recursedepth\endcsname##1##2{#2}%
+ \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel
+ \expandafter\syst_helpers_recurse_indeed\expandafter1\expandafter{\number#1}}
+
+\unexpanded\def\syst_helpers_recurse_y#1#2%
+ {\global\advance\outerrecurse \plusone
+ \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel
+ \let\recurselevel\!!plusone
+ \global\expandafter\def\csname\??recurseaction\recursedepth\endcsname##1##2{#2}%
+ \syst_helpers_recurse_content
+ \expandafter\let\expandafter\recurselevel\csname\??recurseindex\recursedepth\endcsname
+ \global\advance\outerrecurse \minusone}
+
+\unexpanded\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4
+ {\global\advance\outerrecurse \plusone
+ \global\expandafter\def\csname\??recurseaction\recursedepth\endcsname##1##2{#4}%
+ \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel
+ \ifnum#3>\zerocount\relax
+ \ifnum#2<#1\relax
+ \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit
+ \else
+ \let\syst_helpers_stepwise_next\syst_helpers_stepwise_recurse
+ \fi
+ \else
+ \ifnum#3<\zerocount\relax
+ \ifnum#1<#2\relax
+ \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit
+ \else
+ \let\syst_helpers_stepwise_next\syst_helpers_stepwise_reverse
+ \fi
+ \else
+ \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit
+ \fi
+ \fi\normalexpanded{\syst_helpers_stepwise_next{\number#1}{\number#2}{\number#3}}}
+
+\unexpanded\def\doloop#1%
+ {\global\advance\outerrecurse \plusone
+ \global\expandafter\def\csname\??recurseaction\recursedepth\endcsname##1##2{#1}%
+ \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel
+ \let\endofloop\syst_helpers_loop
+ \syst_helpers_loop1} % no \plusone else \recurselevel wrong
+
+% faster
+
+% \unexpanded\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4
+% {\global\advance\outerrecurse \plusone
+% \global\expandafter\def\csname\??recurseaction\recursedepth\endcsname##1##2{#4}%
+% \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel
+% \csname @swr%
+% \ifnum#3>\zerocount
+% \ifnum#2<#1\else d\fi
+% \else\ifnum#3<\zerocount
+% \ifnum#1<#2\else r\fi
+% \fi\fi
+% \expandafter\endcsname\normalexpanded{{\number#1}{\number#2}{\number#3}}}
+
+% \let\@swr \syst_helpers_stepwise_exit
+% \let\@swrd\syst_helpers_stepwise_recurse
+% \let\@swrr\syst_helpers_stepwise_reverse
+
+\installsystemnamespace{recursestepwise}
+
+\unexpanded\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4
+ {\global\advance\outerrecurse \plusone
+ \global\expandafter\def\csname\??recurseaction\recursedepth\endcsname##1##2{#4}%
+ \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel
+ \csname\??recursestepwise
+ \ifnum#3>\zerocount
+ \ifnum#2<#1\else d\fi
+ \else\ifnum#3<\zerocount
+ \ifnum#1<#2\else r\fi
+ \fi\fi
+ \expandafter\endcsname\normalexpanded{{\number#1}{\number#2}{\number#3}}}
+ % \expandafter\endcsname\expandafter{\number#1\expandafter}\expandafter{\number#2\expandafter}\expandafter{\number#3}}
+
+\letvalue{\??recursestepwise }\syst_helpers_stepwise_exit
+\letvalue{\??recursestepwise d}\syst_helpers_stepwise_recurse
+\letvalue{\??recursestepwise r}\syst_helpers_stepwise_reverse
+
+% quite okay too, but untested
+%
+% \def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4
+% {\global\advance\outerrecurse \plusone
+% \global\expandafter\def\csname\??recurseaction\recursedepth\endcsname##1##2{#4}%
+% \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel
+% \normalexpanded
+% {\ifnum#3>\zerocount
+% \ifnum#2<#1
+% \syst_helpers_stepwise_exit
+% \else
+% \syst_helpers_stepwise_recurse
+% \fi
+% \else
+% \ifnum#3<\zerocount
+% \ifnum#1<#2
+% \syst_helpers_stepwise_exit
+% \else
+% \syst_helpers_stepwise_reverse
+% \fi
+% \else
+% \syst_helpers_stepwise_exit
+% \fi
+% \fi{\number#1}{\number#2}{\number#3}}}
+
+%D For special purposes:
+
+\newcount\fastloopindex
+\newcount\fastloopfinal
+
+\let\m_syst_helpers_fast_loop_cs\relax
+
+\unexpanded\def\dofastloopcs#1#2%
+ {\let\m_syst_helpers_fast_loop_cs#2%
+ \fastloopindex\plusone
+ \fastloopfinal#1\relax
+ \syst_helpers_fast_loop_cs}
+
+\unexpanded\def\syst_helpers_fast_loop_cs
+ {\ifnum\fastloopindex>\fastloopfinal
+ \let\m_syst_helpers_fast_loop_cs\relax
+ \else
+ \m_syst_helpers_fast_loop_cs
+ \advance\fastloopindex\plusone
+ \expandafter\syst_helpers_fast_loop_cs
+ \fi}
+
+% Helper:
+
+\unexpanded\def\resetrecurselevel{\let\recurselevel\!!zerocount}
+
+\let\recurselevel\!!zerocount
+
+% \appendtoks \resetrecurselevel \to \everydump
+
+%D \macros
+%D {doloopoverlist}
+%D
+%D \starttyping
+%D \doloopoverlist {red,green,blue} {
+%D \setuppalet[\recursestring]
+%D \doloopoverlist {light,normal,dark} {
+%D \blackrule[color=\recursestring,width=20cm,height=2cm,depth=0cm]\par
+%D }
+%D }
+%D \stoptyping
+%D
+%D or:
+%D
+%D \starttyping
+%D \doloopoverlist {red,green,blue} {
+%D \setuppalet[#1]
+%D \doloopoverlist {light,normal,dark} {
+%D \blackrule[color=##1,width=20cm,height=2cm,depth=0cm]\par
+%D }
+%D }
+%D \stoptyping
+
+\unexpanded\def\doloopoverlist#1#2%
+ {\global\advance\outerrecurse\plusone
+ \expandafter\gdef\csname\??recurseaction\recursedepth\endcsname##1{\edef\recursestring{##1}#2}%
+ \expandafter\glet\csname\??recurseindex\recursedepth\endcsname\recursestring
+ \normalexpanded{\processcommalist[#1]{\expandafter\noexpand\csname\??recurseaction\recursedepth\endcsname}}%
+ \expandafter\let\expandafter\recursestring\csname\??recurseindex\recursedepth\endcsname
+ \global\advance\outerrecurse\minusone}
+
+%D \macros
+%D {newevery,everyline,EveryLine,EveryPar}
+%D
+%D Lets skip to something quite different. It's common use to use \type {\everypar}
+%D for special purposes. In \CONTEXT\ we use this primitive for locating sidefloats.
+%D This means that when user assignments to \type {\everypar} can interfere with
+%D those of the package. We therefore introduce \type {\EveryPar}.
+%D
+%D The same goes for \type {\EveryLine}. Because \TEX\ offers no \type {\everyline}
+%D primitive, we have to call for \type {\everyline} when we are working on a line
+%D by line basis. Just by calling \type {\EveryPar{}} and \type {\EveryLine{}} we
+%D restore the old situation.
+
+% \dorecurse{2}{
+% \expanded{\everypar{before \recurselevel\space}}
+% \EveryPar{x } [before \recurselevel\space x] \par
+% \EveryPar{y } [before \recurselevel\space y] \par
+% \EveryPar{} [before \recurselevel] \par
+% \EveryPar{x } \EveryPar{y } \EveryPar{} [before \recurselevel] \par
+% \EveryPar{y } \everypar{before } [before] \par
+% }
+
+\installsystemnamespace{extraevery}
+
+% \unexpanded\def\newevery#1#2%
+% {\ifx#1\everypar\else\newtoks#1\fi% we test for redefinition elsewhere
+% \ifx#2\relax\else\ifdefined#2\else
+% \expandafter\newtoks\csname\??extraevery\csstring#1\endcsname
+% \def#2{\syst_helpers_every#1}%
+% \fi\fi}
+%
+% \unexpanded\def\syst_helpers_every#1%
+% {\expandafter\removetoks\expandafter\the\csname\??extraevery\csstring#1\endcsname\from#1%
+% \expandafter\appendtoks\expandafter\the\csname\??extraevery\csstring#1\endcsname\to #1%
+% \csname\??extraevery\csstring#1\endcsname}
+
+\unexpanded\def\newevery#1#2%
+ {\ifx#1\everypar\else\newtoks#1\fi% we test for redefinition elsewhere
+ \ifx#2\relax\else\ifdefined#2\else
+ \expandafter\newtoks\csname\??extraevery\csstring#1\endcsname
+ \edef#2{\syst_helpers_every#1\csname\??extraevery\csstring#1\endcsname}%
+ \fi\fi}
+
+\unexpanded\def\syst_helpers_every#1#2%
+ {\removetoks\the#2\from#1%
+ \appendtoks\the#2\to #1%
+ #2}
+
+%D This one permits definitions like:
+
+\newevery \everypar \EveryPar % we get a warning which is ok
+\newevery \everyline \EveryLine
+
+%D and how about:
+
+% \newtoks \neverypar
+% \newtoks \neveryendpar
+%
+% \normalprotected\def\syst_helpers_forgotten_endpar
+% {\the\neveryendpar\normalpar}
+%
+% \unexpanded\def\forgeteverypar
+% {\everypar{\the\neverypar}%
+% \let\endpar\syst_helpers_forgotten_endpar}
+%
+% \unexpanded\def\finishpar
+% {\ifvmode\else\par\fi}
+
+\newtoks \neverypar
+
+\unexpanded\def\forgeteverypar
+ {\everypar{\the\neverypar}}
+
+%D Which we're going to use indeed! When the second argument
+%D equals \type {\relax}, the first token list is created
+%D unless it is already defined.
+
+%D Technically spoken we could have used the method we are
+%D going to present in the visual debugger. First we save
+%D the primitive \type{\everypar}:
+%D
+%D \starttyping
+%D \let\normaleverypar=\everypar
+%D \stoptyping
+%D
+%D Next we allocate a \TOKENLIST\ named \type{\everypar},
+%D which means that \type{\everypar} is no longer a primitive
+%D but something like \type{\toks44}.
+%D
+%D \starttyping
+%D \newtoks\everypar
+%D \stoptyping
+%D
+%D Because \TEX\ now executes \type{\normaleverypar} instead
+%D of \type{\everypar}, we are ready to assign some tokens to
+%D this internally known and used \TOKENLIST.
+%D
+%D \starttyping
+%D \normaleverypar={all the things the system wants to do \the\everypar}
+%D \stoptyping
+%D
+%D Where the user can provide his own tokens to be expanded
+%D every time he expects them to expand.
+%D
+%D \starttyping
+%D \everypar={something the user wants to do}
+%D \stoptyping
+%D
+%D We don't use this method because it undoubtly leads to
+%D confusing situations, especially when other packages are
+%D used, but it's this kind of tricks that make \TEX\ so
+%D powerful.
+
+%D \macros
+%D {convertargument,convertcommand,convertvalue}
+%D
+%D Some persistent experimenting led us to the next macro. This
+%D macro converts a parameter or an expanded macro to it's
+%D textual meaning.
+%D
+%D \starttyping
+%D \convertargument ... \to \command
+%D \stoptyping
+%D
+%D For example,
+%D
+%D \starttyping
+%D \convertargument{one \two \three{four}}\to\ascii
+%D \stoptyping
+%D
+%D The resulting macro \type{\ascii} can be written to a file
+%D or the terminal without problems. In \CONTEXT\ we use this
+%D macro for generating registers and tables of contents.
+%D
+%D The second conversion alternative accepts a command:
+%D
+%D \starttyping
+%D \convertcommand\command\to\ascii
+%D \stoptyping
+%D
+%D Both commands accept the prefix \type{\doglobal} for global
+%D assignments.
+
+\unexpanded\def\convertvalue#1\to
+ {\expandafter\convertcommand\csname#1\endcsname\to}
+
+\unexpanded\def\defconvertedvalue#1#2% less sensitive for \to
+ {\expandafter\defconvertedcommand\expandafter#1\csname#2\endcsname}
+
+%D \macros
+%D {doifassignmentelse}
+%D
+%D A lot of \CONTEXT\ commands take optional arguments, for
+%D instance:
+%D
+%D \starttyping
+%D \dothisorthat[alfa,beta]
+%D \dothisorthat[first=foo,second=bar]
+%D \dothisorthat[alfa,beta][first=foo,second=bar]
+%D \stoptyping
+%D
+%D Although a combined solution is possible, we prefer a
+%D seperation. The next command takes care of propper
+%D handling of such multi||faced commands.
+%D
+%D \starttyping
+%D \doifassignmentelse {...} {then ...} {else ...}
+%D \stoptyping
+
+\def\syst_helpers_check_if_assignment_else#1=#2#3\_e_o_p_{\if#2@}%
+
+\unexpanded\def\doifelseassignment#1% expandable
+ {\expandafter\syst_helpers_check_if_assignment_else\detokenize{#1}=@@\_e_o_p_
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+\let\doifassignmentelse\doifelseassignment
+
+\newif\ifassignment
+
+\unexpanded\def\docheckassignment#1%
+ {\expandafter\syst_helpers_check_if_assignment_else\detokenize{#1}=@@\_e_o_p_
+ \assignmentfalse
+ \else
+ \assignmenttrue
+ \fi}
+
+%D In \ETEX\ we can use \type {\detokenize} and gain some
+%D speed, but in general far less that 1\% for \type
+%D {\convertargument} and nil for \type {\convertcommand}.
+%D This macro is more robust than the pure \TEX\ one,
+%D something I found out when primitives like \type
+%D {\jobname} were fed (or something undefined).
+
+\unexpanded\def\convertargument#1\to#2{\dodoglobal\edef#2{\detokenize{#1}}}
+\unexpanded\def\convertcommand #1\to#2{\dodoglobal\edef#2{\expandafter\detokenize\expandafter{#1}}} % hm, only second is also ok
+
+\unexpanded\def\defconvertedargument #1#2{\edef#1{\detokenize{#2}}}
+\unexpanded\def\defconvertedcommand #1#2{\edef#1{\detokenize\expandafter{#2}}}
+\unexpanded\def\edefconvertedargument#1#2{\edef#1{#2}%
+ \edef#1{\detokenize\expandafter{#1}}}
+\unexpanded\def\gdefconvertedargument#1#2{\xdef#1{\detokenize{#2}}}
+\unexpanded\def\gdefconvertedcommand #1#2{\xdef#1{\detokenize\expandafter{#2}}}
+\unexpanded\def\xdefconvertedargument#1#2{\xdef#1{#2}%
+ \xdef#1{\detokenize\expandafter{#1}}}
+
+%D When you try to convert a primitive command, you'll find
+%D out that the \ETEX\ method fails on for instance \type
+%D {\jobname} in the sense that it returns the filename
+%D instead of just \type {\jobname}. So far this does not
+%D give real problems.
+
+%D This is typically a macro that one comes to after reading
+%D the \TEX book carefully. Even then, the definite solution
+%D was found after rereading the \TEX book. The first
+%D implementation was:
+%D
+%D \starttyping
+%D \def\doconvertargument#1->#2\\\\{#2}
+%D \stoptyping
+%D
+%D The \type{-}, the delimiter \type{\\\\} and the the second
+%D argument are completely redundant.
+
+%D \macros
+%D {showvalue}
+%D
+%D Ahandy macro, for testing purposes only:
+
+\unexpanded\def\showvalue#1%
+ {\ifcsname#1\endcsname
+ \expandafter\show\csname#1\endcsname
+ \else
+ \show\undefined
+ \fi}
+
+%D \macros
+%D {doifmeaningelse}
+%D
+%D We can use both commands in testing, but alas, not all
+%D meanings expand to something \type {->}. This is no problem
+%D in the \ETEX\ implementation, but since we want
+%D compatibility, we need:
+%D
+%D \starttyping
+%D \doifmeaningelse {\next} {\something} {true} {false}
+%D \stoptyping
+%D
+%D Watch the one level expansion of the second argument.
+
+\unexpanded\def\doifelsemeaning#1#2%
+ {\edef\m_syst_string_one{\meaning#1}%
+ \def \m_syst_string_two{#2}%
+ \edef\m_syst_string_two{\meaning\m_syst_string_two}%
+ \ifx\m_syst_string_one\m_syst_string_two
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\let\doifmeaningelse\doifelsemeaning
+
+%D \macros
+%D {doifsamestringselse,doifsamestring,doifnotsamestring}
+%D
+%D The next comparison macro converts the arguments into
+%D expanded strings. This command can be used to compare for
+%D instance \type {\jobname} with a name stored in a macro.
+%D
+%D \starttyping
+%D \doifelse {\jobname}{oeps}{YES}{NO}
+%D \doifsamestringelse{\jobname}{oeps}{YES}{NO}
+%D \stoptyping
+
+\def\syst_helpers_if_samestring_else#1#2#3#4%
+ {\edef\m_syst_string_one{\detokenize\expandafter{\normalexpanded{#3}}}%
+ \edef\m_syst_string_two{\detokenize\expandafter{\normalexpanded{#4}}}%
+ \ifx\m_syst_string_one\m_syst_string_two\expandafter#1\else\expandafter#2\fi}
+
+\unexpanded\def\doifelsesamestring{\syst_helpers_if_samestring_else\firstoftwoarguments\secondoftwoarguments}
+\unexpanded\def\doifsamestring {\syst_helpers_if_samestring_else\firstofoneargument \gobbleoneargument }
+\unexpanded\def\doifnotsamestring {\syst_helpers_if_samestring_else\gobbleoneargument \firstofoneargument }
+
+\let\doifsamestringelse\doifelsesamestring
+
+%D \macros
+%D {ConvertToConstant,ConvertConstantAfter}
+%D
+%D When comparing arguments with a constant, we can get into
+%D trouble when this argument consists of tricky expandable
+%D commands. One solution for this is converting the
+%D argument to a string of unexpandable characters. To make
+%D comparison possible, we have to convert the constant too
+%D
+%D \starttyping
+%D \ConvertToConstant\doifelse {...} {...} {then ...} {else ...}
+%D \stoptyping
+%D
+%D This construction is only needed when the first argument
+%D can give troubles. Misuse can slow down processing.
+%D
+%D \starttyping
+%D \ConvertToConstant\doifelse{\c!alfa} {\c!alfa}{...}{...}
+%D \ConvertToConstant\doifelse{alfa} {\c!alfa}{...}{...}
+%D \ConvertToConstant\doifelse{alfa} {alfa} {...}{...}
+%D \ConvertToConstant\doifelse{alfa \alfa test}{\c!alfa}{...}{...}
+%D \stoptyping
+%D
+%D In examples~2 and~3 both arguments equal, in~1 and~4
+%D they differ.
+
+\unexpanded\def\ConvertToConstant#1#2#3%
+ {\edef\m_syst_string_one{\expandafter\detokenize\expandafter{#2}}%
+ \edef\m_syst_string_two{\expandafter\detokenize\expandafter{#3}}%
+ #1{\m_syst_string_one}{\m_syst_string_two}}
+
+%D When the argument \type{#1} consists of commands, we had
+%D better use
+%D
+%D \starttyping
+%D \ConvertConstantAfter\processaction[#1][...]
+%D \ConvertConstantAfter\doifelse{#1}{\v!something}{}{}
+%D \stoptyping
+%D
+%D This commands accepts things like:
+%D
+%D \starttyping
+%D \v!constant
+%D constant
+%D \hbox to \hsize{\rubish}
+%D \stoptyping
+%D
+%D As we will see in the core modules, this macro permits
+%D constructions like:
+%D
+%D \starttyping
+%D \setupfootertexts[...][...]
+%D \setupfootertexts[margin][...][...]
+%D \setupfootertexts[\v!margin][...][...]
+%D \stoptyping
+%D
+%D where \type{...} can be anything legally \TEX.
+
+\unexpanded\def\CheckConstantAfter#1#2%
+ {\expandafter\convertargument\v!prefix!\to\ascii
+ \convertargument#1\to#2\relax
+ \doifelseinstring\ascii{#2}
+ {\expandafter\convertargument#1\to#2}
+ {}}
+
+\unexpanded\def\ConvertConstantAfter#1#2#3%
+ {\CheckConstantAfter{#2}\asciia
+ \CheckConstantAfter{#3}\asciib
+ #1{\asciia}{\asciib}}
+
+%D \macros
+%D {assignifempty}
+%D
+%D We can assign a default value to an empty macro using:
+%D
+%D \starttyping
+%D \assignifempty \macros {default value}
+%D \stoptyping
+%D
+%D We don't explicitly test if the macro is defined.
+
+\unexpanded\def\assignifempty#1#2% can be sped up
+ {\doifsomething{#1}{\def#1{#2}}} % {\doifnot{#1}{}{\def#1{#2}}}
+
+%D \macros
+%D {gobbleuntil,grabuntil,gobbleuntilrelax,
+%D processbetween,processuntil}
+%D
+%D In \TEX\ gobbling usually stand for skipping arguments, so
+%D here are our gobbling macros.
+%D
+%D In \CONTEXT\ we use a lot of \type{\start}||\type{\stop}
+%D like constructions. Sometimes, the \type{\stop} is used as a
+%D hard coded delimiter like in:
+%D
+%D \starttyping
+%D \unexpanded\def\startcommand#1\stopcommand%
+%D {... #1 ...}
+%D \stoptyping
+%D
+%D In many cases the \type{\start}||\type{\stop} pair is
+%D defined at format generation time or during a job. This
+%D means that we cannot hardcode the \type{\stop} criterium.
+%D Only after completely understanding \type{\csname} and
+%D \type{\expandafter} I was able to to implement a solution,
+%D starting with:
+%D
+%D \starttyping
+%D \grabuntil{stop}\command
+%D \stoptyping
+%D
+%D This commands executes, after having encountered
+%D \type {\stop} the command \type {\command}. This command
+%D receives as argument the text preceding the \type {\stop}.
+%D This means that:
+%D
+%D \starttyping
+%D \unexpanded\def\starthello%
+%D {\grabuntil{stophello}\message}
+%D
+%D \starthello Hello world!\stophello
+%D \stoptyping
+%D
+%D results in: \type{\message{Hello world!}}.
+
+\let\syst_helpers_grab_indeed\relax
+
+\unexpanded\def\syst_helpers_grab#1#2%
+ {\def\syst_helpers_grab_indeed##1#1{#2{##1}}\syst_helpers_grab_indeed}
+
+\unexpanded\def\grabuntil#1%
+ {\expandafter\syst_helpers_grab\expandafter{\csname#1\endcsname}}
+
+%D The next command build on this mechanism:
+%D
+%D \starttyping
+%D \processbetween{string}\command
+%D \stoptyping
+%D
+%D Here:
+%D
+%D \starttyping
+%D \processbetween{hello}\message
+%D \starthello Hello again!\stophello
+%D \stoptyping
+%D
+%D leads to: \type{\message{Hello again!}}. The command
+%D
+%D \starttyping
+%D \gobbleuntil{sequence}
+%D \stoptyping
+%D
+%D is related to these commands. This one simply throws away
+%D everything preceding \type{\command}.
+
+\let\syst_helpers_gobble_indeed\relax
+
+\unexpanded\def\processbetween#1#2%
+ {\setvalue{\s!start#1}{\grabuntil{\s!stop#1}{#2}}}
+
+\unexpanded\def\gobbleuntil#1%
+ {\def\syst_helpers_gobble_indeed##1#1{}\syst_helpers_gobble_indeed}
+
+\unexpanded\def\gobbleuntilrelax#1\relax
+ {}
+
+%D The next one simply expands the pickup up tokens.
+%D
+%D \starttyping
+%D \processuntil{sequence}
+%D \stoptyping
+
+\let\syst_helpers_until_indeed\relax
+
+\unexpanded\def\processuntil#1%
+ {\def\syst_helpers_until_indeed##1#1{##1}\syst_helpers_until_indeed}
+
+%D \macros
+%D {groupedcommand}
+%D
+%D Commands often manipulate argument as in:
+%D
+%D \starttyping
+%D \def\doezomaarwat#1{....#1....}
+%D \stoptyping
+%D
+%D A disadvantage of this approach is that the tokens that
+%D form \type{#1} are fixed the the moment the argument is read
+%D in. Normally this is no problem, but for instance verbatim
+%D environments adapt the \CATCODES\ of characters and therefore
+%D are not always happy with already fixed tokens.
+%D
+%D Another problem arises when the argument is grouped not by
+%D \type{{}} but by \type{\bgroup} and \type{\egroup}. Such an
+%D argument fails, because the \type{\bgroup} is een as the
+%D argument (which is quite normal).
+%D
+%D The next macro offers a solution for both unwanted
+%D situations:
+%D
+%D \starttyping
+%D \groupedcommand {before} {after}
+%D \stoptyping
+%D
+%D Which can be used like:
+%D
+%D \starttyping
+%D \def\cite%
+%D {\groupedcommand{\rightquote\rightquote}{\leftquote\leftquote}}
+%D \stoptyping
+%D
+%D This command is equivalent to, but more 'robust' than:
+%D
+%D \starttyping
+%D \def\cite#1%
+%D {\rightquote\rightquote#1\leftquote\leftquote}
+%D \stoptyping
+%D
+%D \starttyping
+%D \def\rightword%
+%D {\groupedcommand{\hfill\hbox}{\parfillskip\zeropoint}}
+%D
+%D .......... \rightword{the right way}
+%D \stoptyping
+%D
+%D Here \TEX\ typesets \type{\bf the right way} unbreakable
+%D at the end of the line. The solution mentioned before does
+%D not work here. We also handle
+%D
+%D \starttyping
+%D to be \bold{bold} or not, that's the question
+%D \stoptyping
+%D
+%D and
+%D
+%D \starttyping
+%D to be {\bold bold} or not, that's the question
+%D \stoptyping
+%D
+%D This alternative checks for a \type {\bgroup} token first. The internal
+%D alternative does not accept the box handling mentioned before, but further
+%D nesting works all right. The extra \type {\bgroup}||\type {\egroup} is needed to
+%D keep \type {\m_syst_helpers_handle_group_after} both into sight and local.
+
+\let\m_syst_helpers_handle_group_after \relax
+\let\m_syst_helpers_handle_group_before\relax
+
+% keep:
+%
+% \unexpanded\def\syst_helpers_handle_group_normal#1#2%
+% {\bgroup
+% \def\m_syst_helpers_handle_group_before{\bgroup#1\bgroup\aftergroup\m_syst_helpers_handle_group_after}% can't we remove the second \bgroup
+% \def\m_syst_helpers_handle_group_after {#2\egroup\egroup}% and one \egroup here?
+% \afterassignment\m_syst_helpers_handle_group_before
+% \let\next=}
+
+\unexpanded\def\syst_helpers_handle_group_normal#1#2%
+ {\bgroup
+ \def\m_syst_helpers_handle_group_before{#1}%
+ \def\m_syst_helpers_handle_group_after {#2}%
+ \afterassignment\m_syst_helpers_handle_group_normal_before
+ \let\next=}
+
+\def\m_syst_helpers_handle_group_normal_before
+ {\bgroup
+ \m_syst_helpers_handle_group_before
+ \bgroup
+ \aftergroup\m_syst_helpers_handle_group_normal_after}
+
+\def\m_syst_helpers_handle_group_normal_after
+ {\m_syst_helpers_handle_group_after
+ \egroup
+ \egroup}
+
+% keep:
+%
+% \unexpanded\def\syst_helpers_handle_group_simple#1#2% no inner group (so no kerning interference)
+% {\bgroup
+% %def\m_syst_helpers_handle_group_before{\bgroup#1\aftergroup\m_syst_helpers_handle_group_after}% interferes
+% \def\m_syst_helpers_handle_group_before{\bgroup\aftergroup\m_syst_helpers_handle_group_after#1}%
+% \def\m_syst_helpers_handle_group_after {#2\egroup}%
+% \afterassignment\m_syst_helpers_handle_group_before
+% \let\next=}
+
+\unexpanded\def\syst_helpers_handle_group_simple#1#2% no inner group (so no kerning interference)
+ {\bgroup
+ \def\m_syst_helpers_handle_group_before{#1}%
+ \def\m_syst_helpers_handle_group_after {#2}%
+ \afterassignment\m_syst_helpers_handle_group_simple_before
+ \let\next=}
+
+\def\m_syst_helpers_handle_group_simple_before
+ {\bgroup
+ \aftergroup\m_syst_helpers_handle_group_simple_after
+ \m_syst_helpers_handle_group_before}
+
+\def\m_syst_helpers_handle_group_simple_after
+ {\m_syst_helpers_handle_group_after
+ \egroup}%
+
+\unexpanded\def\syst_helpers_handle_group_pickup#1#2#3% no inner group (so no kerning interference)
+ {\bgroup
+ \def\m_syst_helpers_handle_group_before{#1}%
+ \def\m_syst_helpers_handle_group_after {#2\egroup#3}%
+ \afterassignment\m_syst_helpers_handle_group_pickup_before
+ \let\next=}
+
+\def\m_syst_helpers_handle_group_pickup_before
+ {\bgroup
+ \aftergroup\m_syst_helpers_handle_group_after
+ \m_syst_helpers_handle_group_before}
+
+\unexpanded\def\syst_helpers_handle_group_nop
+ {\ifnum\currentgrouptype=\semisimplegroupcode
+ \expandafter\syst_helpers_handle_group_nop_a
+ \else
+ \expandafter\syst_helpers_handle_group_nop_b
+ \fi}
+
+\def\syst_helpers_handle_group_nop_a#1#2%
+ {\def\m_syst_helpers_handle_group_after{#2\endgroup}%
+ \begingroup
+ \aftergroup\m_syst_helpers_handle_group_after
+ #1}
+
+\def\syst_helpers_handle_group_nop_b#1#2%
+ {\def\m_syst_helpers_handle_group_after{#2\egroup}%
+ \bgroup
+ \aftergroup\m_syst_helpers_handle_group_after
+ #1}
+
+%D I considered it a nuisance that
+%D
+%D \starttyping
+%D \color[green]
+%D {as grass}
+%D \stoptyping
+%D
+%D was not interpreted as one would expect. This is due to the
+%D fact that \type{\futurelet} obeys blank spaces, and a
+%D line||ending token is treated as a blank space. So the final
+%D implementation became:
+
+\unexpanded\def\groupedcommand#1#2%
+ {\doifelsenextbgroup{\syst_helpers_handle_group_normal{#1}{#2}}{\syst_helpers_handle_group_nop{#1}{#2}}}
+
+\unexpanded\def\simplegroupedcommand#1#2%
+ {\doifelsenextbgroup{\syst_helpers_handle_group_simple{#1}{#2}}{\syst_helpers_handle_group_nop{#1}{#2}}}
+
+\unexpanded\def\pickupgroupedcommand#1#2#3%
+ {\doifelsenextbgroup{\syst_helpers_handle_group_pickup{#1}{#2}{#3}}{\syst_helpers_handle_group_nop{#1}{#2}}}
+
+%D Users should be aware of the fact that grouping can
+%D interfere with ones paragraph settings that are executed
+%D after the paragraph is closed. One should therefore
+%D explictly close the paragraph with \type{\par}, else the
+%D settings will be forgotten and not applied. So it's:
+%D
+%D \starttyping
+%D \def\BoldRaggedCenter%
+%D {\groupedcommand{\raggedcenter\bf}{\par}}
+%D \stoptyping
+
+%D \macros
+%D {checkdefined}
+%D
+%D The bigger the system, the greater the change that
+%D user defined commands collide with those that are part of
+%D the system. The next macro gives a warning when a command is
+%D already defined. We considered blocking the definition, but
+%D this is not always what we want.
+%D
+%D \starttyping
+%D \checkdefined {category} {class} {command}
+%D \stoptyping
+%D
+%D The user is warned with the suggestion to use
+%D \type{CAPITALS}. This suggestion is feasible, because
+%D \CONTEXT only defines lowcased macros.
+
+\unexpanded\def\showdefinederror#1#2%
+ {\writestatus\m!system{#1 #2 replaces a macro, use CAPITALS!}}
+
+\unexpanded\def\checkdefined#1#2#3%
+ {\doifdefined{#3}{\showdefinederror{#2}{#3}}}
+
+%D \macros
+%D {GotoPar,GetPar}
+%D
+%D Typesetting a paragraph in a special way can be done by
+%D first grabbing the contents of the paragraph and processing
+%D this contents grouped. The next macro for instance typesets
+%D a paragraph in boldface.
+%D
+%D \starttyping
+%D \def\remark#1\par%
+%D {\bgroup\bf#1\egroup}
+%D \stoptyping
+%D
+%D This macro has to be called like
+%D
+%D \starttyping
+%D \remark some text ... ending with \par
+%D \stoptyping
+%D
+%D Instead of \type{\par} we can of course use an empty line.
+%D When we started typesetting with \TEX, we already had
+%D produced lots of text in plain \ASCII. In producing such
+%D simple formatted texts, we adopted an open layout, and when
+%D switching to \TEX, we continued this open habit. Although
+%D \TEX\ permits a cramped and badly formatted source, it adds
+%D to confusion and sometimes introduces errors. So we prefer:
+%D
+%D \starttyping
+%D \remark
+%D
+%D some text ... ending with an empty line
+%D \stoptyping
+%D
+%D We are going to implement a mechanism that allows such open
+%D specifications. The definition of the macro handling
+%D \type{\remark} becomes:
+%D
+%D \starttyping
+%D \def\remark%
+%D {\BeforePar{\bgroup\bf}%
+%D \AfterPar{\egroup}%
+%D \GetPar}
+%D \stoptyping
+%D
+%D A macro like \type{\GetPar} can be defined in several
+%D ways. The recent version, the fourth one in a row,
+%D originally was far more complicated, but some functionality
+%D has been moved to other macros.
+%D
+%D We start with the more simple but in some cases more
+%D appropriate alternative is \type{\GotoPar}. This one leaves
+%D \type{\par} unchanged and is therefore more robust. On the
+%D other hand, \type{\AfterPar} is not supported.
+
+\newtoks\BeforePar
+\newtoks\AfterPar
+
+\def\redowithpar\par
+ {\doifelsenextchar\par\redowithpar\dodowithpar}%
+
+\def\dowithpar#1#2%
+ {\def\dodowithpar##1\par{#1##1#2}%
+ \redowithpar\par}
+
+\def\redogotopar\par
+ {\doifelsenextchar\par\redogotopar\dodogotopar}%
+
+\def\dogotopar#1%
+ {\def\dodogotopar{#1}%
+ \redogotopar\par}
+
+\def\dogotoparcs#1%
+ {\let\dodogotopar#1%
+ \redogotopar\par}
+
+\unexpanded\def\GetPar
+ {\expanded
+ {\dowithpar
+ {\the\BeforePar
+ \BeforePar\emptytoks}
+ {\the\AfterPar
+ \BeforePar\emptytoks
+ \AfterPar\emptytoks}}}
+
+\unexpanded\def\GotoPar
+ {\expanded
+ {\dogotopar
+ {\the\BeforePar
+ \BeforePar\emptytoks}}}
+
+%D \macros
+%D {dowithpargument,dowithwargument}
+%D
+%D The next macros are a variation on \type{\GetPar}. When
+%D macros expect an argument, it interprets a grouped sequence
+%D of characters a one token. While this adds to robustness and
+%D less ambiguous situations, we sometimes want to be a bit
+%D more flexible, or at least want to be a bit more tolerant
+%D to user input.
+%D
+%D We start with a commands that acts on paragraphs. This
+%D command is called as:
+%D
+%D \starttyping
+%D \dowithpargument\command
+%D \dowithpargument{\command ... }
+%D \stoptyping
+%D
+%D In \CONTEXT\ we use this one to read in the titles of
+%D chapters, sections etc. The commands responsible for these
+%D activities accept several alternative ways of argument
+%D passing. In these examples, the \type{\par} can be omitted
+%D when an empty line is present.
+%D
+%D \starttyping
+%D \command{...}
+%D \command ... \par
+%D \command
+%D {...}
+%D \command
+%D ... \par
+%D \stoptyping
+
+\let\syst_helpers_next_par\relax
+\let\syst_helpers_next_arg\relax
+
+\unexpanded\def\dowithpargument#1%
+ {\def\syst_helpers_next_par##1 \par{#1{##1}}%
+ \def\syst_helpers_next_arg##1{#1{##1}}%
+ \doifelsenextbgroup\syst_helpers_next_arg{\doifelsenextchar\par{#1{}}\syst_helpers_next_par}}
+
+%D The \type{p} in the previous command stands for paragraph.
+%D When we want to act upon words we can use the \type{w}
+%D alternative.
+%D
+%D \starttyping
+%D \dowithwargument\command
+%D \dowithwargument{... \command ...}
+%D \stoptyping
+%D
+%D The main difference bwteen two alternatives is in the
+%D handling of \type{\par}'s. This time the space token acts
+%D as a delimiter.
+%D
+%D \starttyping
+%D \command{...}
+%D \command ...
+%D \command
+%D {...}
+%D \command
+%D ...
+%D \stoptyping
+
+\let\syst_helpers_next_war\relax
+\let\syst_helpers_next_arg\relax
+
+\unexpanded\def\dowithwargument#1%
+ {\def\syst_helpers_next_war##1 {#1{##1}}%
+ \def\syst_helpers_next_arg##1{#1{##1}}%
+ \doifelsenextbgroup\syst_helpers_next_arg\syst_helpers_next_war}
+
+%D \macros
+%D {dorepeat,dorepeatwithcommand}
+%D
+%D When doing repetitive tasks, we stromgly advice to use
+%D \type{\dorecurse}. The next alternative however, suits
+%D better some of the \CONTEXT\ interface commands.
+%D
+%D \starttyping
+%D \dorepeat[n*\command]
+%D \stoptyping
+%D
+%D The value of the used \COUNTER\ can be called within
+%D \type{\command} by \type{\repeater}.
+%D
+%D A slightly different alternative is:
+%D
+%D \starttyping
+%D \dorepeatwithcommand[n*{...}]\command
+%D \stoptyping
+%D
+%D When we call for something like:
+%D
+%D \starttyping
+%D \dorepeatwithcommand[3*{Hello}]\message
+%D \stoptyping
+%D
+%D we get ourselves three \type{\message{Hello}} messages in
+%D a row. In both commands, the \type{n*} is optional. When this
+%D specification is missing, the command executes once.
+
+\unexpanded\def\dorepeatwithcommand[#1]%
+ {\syst_helpers_repeat_with_command#1*\empty*\relax}
+
+\def\syst_helpers_repeat_with_command#1*#2#3*#4\relax#5%
+ {\ifx#2\empty\syst_helpers_repeat_with_command_again[#1]#5\else\syst_helpers_repeat_with_command_indeed{#1}{#2}{#3}#5\fi}
+
+\def\syst_helpers_repeat_with_command_indeed#1#2#3#4%
+ {\ifx#2\empty % redundant but gives cleaner extensions
+ #4{#1}%
+ \else\ifnum#1<\zerocount
+ %\normalexpanded{\dorecurse{\number-\number#1}}{#4{-#2#3}}%
+ \dorecurse{-#1}{#4{-#2#3}}%
+ \else\ifx#2+%
+ \dorecurse{#1}{#4{#3}}%
+ \else
+ \dorecurse{#1}{#4{#2#3}}%
+ \fi\fi\fi}
+
+\def\syst_helpers_repeat_with_command_again[#1]#2%
+ {#2{#1}}
+
+%D The extension hook permits something like:
+%D
+%D \starttyping
+%D \bgroup
+%D
+%D \catcode`\*=\superscriptcatcode
+%D
+%D \gdef\syst_helpers_repeat_with_command_again[#1]%
+%D {\redodorepeatwithcommand#1*\empty*\relax}
+%D
+%D \gdef\redodorepeatwithcommand#1*#2#3*#4\relax#5%
+%D {\syst_helpers_repeat_with_command_indeed{#1}{#2}{#3}#5}
+%D
+%D \egroup
+%D \stoptyping
+%D
+%D although one may wonder if changing the catcode of \type {*} is wise.
+
+%D \macros
+%D {doifstringinstringelse}
+%D
+%D The next macro is meant for situations where both strings
+%D are macros. This save some unneeded expansion.
+%D
+%D \starttyping
+%D \def\doifstringinstringelse#1#2%
+%D {\syst_helpers_do_if_in_string_else#1#2%
+%D \expandafter\firstoftwoarguments
+%D \else
+%D \expandafter\secondoftwoarguments
+%D \fi}
+%D \stoptyping
+%D
+%D A bit faster is:
+
+\def\syst_helpers_if_instring_else_indeed#1%
+ {\if#1@%
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+\def\doifelsestringinstring#1#2%
+ {\expandafter\def\expandafter\syst_helpers_if_instring_else\expandafter##\expandafter1#1##2##3\_e_o_s_
+ {\syst_helpers_if_instring_else_indeed##2}%
+ \expandafter\expandafter\expandafter\syst_helpers_if_instring_else\expandafter#2#1@@\_e_o_s_}
+
+\let\doifstringinstringelse\doifelsestringinstring
+
+%D \macros
+%D {appendtoks,prependtoks,appendtoksonce,prependtoksonce,
+%D doifintokselse,flushtoks,dotoks}
+%D
+%D We use \TOKENLISTS\ sparsely within \CONTEXT, because the
+%D comma separated lists are more suitable for the user
+%D interface. Nevertheless we have:
+%D
+%D \starttyping
+%D (\doglobal) \appendtoks ... \to\tokenlist
+%D (\doglobal) \prependtoks ... \to\tokenlist
+%D (\doglobal) \flushtoks\tokenlist
+%D \dotoks\tokenlist
+%D \stoptyping
+%D
+%D These macros are clones of the ones implemented in page~378 of
+%D Knuth's \TeX book.
+
+\newtoks\t_syst_helpers_scratch
+\let \m_syst_helpers_scratch\empty
+
+% no longer \def but \let to target toks .. the space gobbling \relax will go
+
+\unexpanded\def\appendtoks {\syst_helpers_append_toks \relax}
+\unexpanded\def\prependtoks {\syst_helpers_prepend_toks \relax}
+\unexpanded\def\appendtoksonce {\syst_helpers_append_toks_once \relax}
+\unexpanded\def\prependtoksonce{\syst_helpers_prepend_toks_once\relax}
+
+\def\syst_helpers_append_toks_indeed
+ {\dodoglobal\m_syst_helpers_scratch\doubleexpandafter{\expandafter\the\expandafter\m_syst_helpers_scratch\the\t_syst_helpers_scratch}}
+
+\def\syst_helpers_prepend_toks_indeed
+ {\dodoglobal\m_syst_helpers_scratch\doubleexpandafter{\expandafter\the\expandafter\t_syst_helpers_scratch\the\m_syst_helpers_scratch}}
+
+\def\syst_helpers_append_toks#1\to#2%
+ {\let\m_syst_helpers_scratch#2%
+ \t_syst_helpers_scratch\expandafter{\gobbleoneargument#1}%
+ \syst_helpers_append_toks_indeed}
+
+\def\syst_helpers_prepend_toks#1\to#2%
+ {\let\m_syst_helpers_scratch#2%
+ \t_syst_helpers_scratch\expandafter{\gobbleoneargument#1}%
+ \syst_helpers_prepend_toks_indeed}
+
+\def\syst_helpers_append_toks_once#1\to#2%
+ {\let\m_syst_helpers_scratch#2%
+ \t_syst_helpers_scratch\expandafter{\gobbleoneargument#1}%
+ \doifelseintoks\t_syst_helpers_scratch\m_syst_helpers_scratch
+ \donothing
+ \syst_helpers_append_toks_indeed}
+
+\def\syst_helpers_prepend_toks_once#1\to#2%
+ {\let\m_syst_helpers_scratch#2%
+ \t_syst_helpers_scratch\expandafter{\gobbleoneargument#1}%
+ \doifelseintoks\t_syst_helpers_scratch\m_syst_helpers_scratch
+ \donothing
+ \syst_helpers_prepend_toks_indeed}
+
+\ifdefined\toksapp
+
+ % \def\syst_helpers_append_toks#1\to#2%
+ % {\toksapp#2\expandafter{\gobbleoneargument#1}%
+ % \ifx\dodoglobal\relax\else
+ % \global#2#2%
+ % \fi}
+ %
+ % \def\syst_helpers_prepend_toks#1\to#2%
+ % {\tokspre#2\expandafter{\gobbleoneargument#1}%
+ % \ifx\dodoglobal\relax\else
+ % \global#2#2%
+ % \fi}
+ %
+ % \def\syst_helpers_append_toks_indeed
+ % {\toksapp\m_syst_helpers_scratch\t_syst_helpers_scratch
+ % \ifx\dodoglobal\relax\else
+ % \global\m_syst_helpers_scratch\m_syst_helpers_scratch
+ % \fi}
+ %
+ % \def\syst_helpers_prepend_toks_indeed
+ % {\tokspre\m_syst_helpers_scratch\t_syst_helpers_scratch
+ % \ifx\dodoglobal\relax\else
+ % \global\m_syst_helpers_scratch\m_syst_helpers_scratch
+ % \fi}
+ %
+ % \def\syst_helpers_append_toks_once#1\to#2%
+ % {\let\m_syst_helpers_scratch#2%
+ % \t_syst_helpers_scratch\expandafter{\gobbleoneargument#1}%
+ % \doifelseintoks\t_syst_helpers_scratch\m_syst_helpers_scratch
+ % \donothing
+ % \syst_helpers_append_toks_indeed}
+ %
+ % \def\syst_helpers_prepend_toks_once#1\to#2%
+ % {\let\m_syst_helpers_scratch#2%
+ % \t_syst_helpers_scratch\expandafter{\gobbleoneargument#1}%
+ % \doifelseintoks\t_syst_helpers_scratch\m_syst_helpers_scratch
+ % \donothing
+ % \syst_helpers_prepend_toks_indeed}
+
+ \unexpanded\def\appendtoks#1\to#2%
+ {\toksapp#2{#1}%
+ \ifx\dodoglobal\relax\else
+ \global#2#2%
+ \fi}
+
+ \unexpanded\def\prependtoks#1\to#2%
+ {\tokspre#2{#1}%
+ \ifx\dodoglobal\relax\else
+ \global#2#2%
+ \fi}
+
+ \def\syst_helpers_append_toks_indeed
+ {\toksapp\m_syst_helpers_scratch\t_syst_helpers_scratch
+ \ifx\dodoglobal\relax\else
+ \global\m_syst_helpers_scratch\m_syst_helpers_scratch
+ \fi}
+
+ \def\syst_helpers_prepend_toks_indeed
+ {\tokspre\m_syst_helpers_scratch\t_syst_helpers_scratch
+ \ifx\dodoglobal\relax\else
+ \global\m_syst_helpers_scratch\m_syst_helpers_scratch
+ \fi}
+
+ \unexpanded\def\appendtoksonce#1\to#2%
+ {\let\m_syst_helpers_scratch#2%
+ \t_syst_helpers_scratch{#1}%
+ \doifelseintoks\t_syst_helpers_scratch\m_syst_helpers_scratch
+ \donothing
+ \syst_helpers_append_toks_indeed}
+
+ \unexpanded\def\prependtoksonce#1\to#2%
+ {\let\m_syst_helpers_scratch#2%
+ \t_syst_helpers_scratch{#1}%
+ \doifelseintoks\t_syst_helpers_scratch\m_syst_helpers_scratch
+ \donothing
+ \syst_helpers_prepend_toks_indeed}
+
+\fi
+
+%D The test macro:
+
+\unexpanded\def\doifelseintoks#1#2% #1 en #2 zijn toks
+ {\edef\asciia{\detokenize\expandafter{\the#1}}%
+ \edef\asciib{\detokenize\expandafter{\the#2}}%
+ \doifelsestringinstring\asciia\asciib}
+
+\let\doifintokselse\doifelseintoks
+
+%D A nice one too:
+
+% {\scratchtoks{abc} \removetoks b\from\scratchtoks [\the\scratchtoks]}
+% {\scratchtoks{abc} \removetoks x\from\scratchtoks [\the\scratchtoks]}
+% {\scratchtoks{} \removetoks x\from\scratchtoks [\the\scratchtoks]}
+% {\scratchtoks{xaa} \removetoks x\from\scratchtoks [\the\scratchtoks]}
+% {\scratchtoks{a\relax b} \removetoks \relax\from\scratchtoks [\showthe\scratchtoks]}
+
+\unexpanded\def\removetoks#1\from#2%
+ {\def\syst_helpers_remove_toks##1#1##2\empty\empty\empty##3\_e_o_t_
+ {\def\m_syst_string_one{##3}%
+ \ifx\m_syst_string_one\empty#2{##1}\else#2{##1##2}\fi}%
+ \expandafter\syst_helpers_remove_toks\the#2\empty\empty\empty#1\empty\empty\empty\_e_o_t_}
+
+%D Also:
+
+\unexpanded\def\appendetoks #1\to{\normalexpanded{\appendtoks #1}\to}
+\unexpanded\def\prependetoks#1\to{\normalexpanded{\prependtoks#1}\to}
+
+\ifdefined\toksapp
+
+ \def\appendetoks#1\to#2%
+ {\etoksapp#2{#1}%
+ \ifx\dodoglobal\relax\else
+ \global#2#2%
+ \fi}
+
+ \def\prependetoks#1\to#2%
+ {\etokspre#2{#1}%
+ \ifx\dodoglobal\relax\else
+ \global#2#2%
+ \fi}
+
+\fi
+
+%D Hm.
+
+\unexpanded\def\flushtoks#1% nb: can reassing to #1 again, hence the indirectness
+ {\t_syst_helpers_scratch#1\relax
+ \dodoglobal#1\emptytoks
+ \the\t_syst_helpers_scratch\relax}
+
+% better: \def\flushtoks#1{\normalexpanded{\noexpand\dodoglobal#1\emptytoks\the#\relax}}
+
+\let\dotoks\the
+
+%D \macros
+%D {beforesplitstring,aftersplitstring}
+%D
+%D These both commands split a string at a given point in two
+%D parts, so \type{x.y} becomes \type{x} or \type{y}.
+%D
+%D \starttyping
+%D \beforesplitstring test.tex\at.\to\filename
+%D \aftersplitstring test.tex\at.\to\extension
+%D \stoptyping
+%D
+%D The first routine looks (and is indeed) a bit simpler than
+%D the second one. The alternative looking more or less like
+%D the first one did not always give the results we needed.
+%D Both implementations show some insight in the manipulation
+%D of arguments.
+
+\let\syst_helpers_split_string\relax
+
+\unexpanded\def\beforesplitstring#1\at#2\to#3%
+ {\def\syst_helpers_split_string##1#2##2#2##3\\%
+ {\def#3{##1}}%
+ \expandafter\syst_helpers_split_string#1#2#2\\}
+
+\unexpanded\def\aftersplitstring#1\at#2\to#3%
+ {\def\syst_helpers_split_string##1#2##2@@@##3\\%
+ {\def#3{##2}}%
+ \expandafter\syst_helpers_split_string#1@@@#2@@@\\}
+
+%D \macros
+%D {splitstring,greedysplitstring}
+%D
+%D A bonus macro.
+
+\unexpanded\def\splitstring#1\at#2\to#3\and#4%
+ {\def\syst_helpers_split_string##1#2##2\empty\empty\empty##3\\%
+ {\def#3{##1}%
+ \def\syst_helpers_split_string{##3}%
+ \ifx\syst_helpers_split_string\empty
+ \let#4\empty
+ \else
+ \def#4{##2}%
+ \fi}%
+ \expandafter\syst_helpers_split_string#1\empty\empty\empty#2\empty\empty\empty\\}
+
+\unexpanded\def\greedysplitstring#1\at#2\to#3\and#4%
+ {\edef\asciib{#1}%
+ \let\asciic\asciib
+ \let#3\empty
+ \let#4\empty
+ \doloop
+ {\expandafter\splitstring\asciib\at#2\to\asciia\and\asciib
+ \ifx\asciib\empty
+ \exitloop
+ \else
+ % not \edef#3{\ifx#3\empty\else#3#2\fi\asciia} else
+ % /root/path fails because then #3==empty
+ \edef#3{\ifcase\recurselevel\or\else#3#2\fi\asciia}%
+ \let#4\asciib
+ \fi}%
+ \ifx#3\empty\let#3\asciic\fi}
+
+%D \macros
+%D {beforetestandsplitstring,
+%D aftertestandsplitstring,
+%D testandsplitstring}
+
+\unexpanded\def\beforetestandsplitstring#1\at#2\to#3%
+ {\def\syst_helpers_split_string##1#2##2#2##3##4\\%
+ {\ifx##3\empty\let#3\empty\else\def#3{##1}\fi}%
+ \expandafter\syst_helpers_split_string#1#2#2\empty\\}
+
+\unexpanded\def\aftertestandsplitstring#1\at#2\to#3%
+ {\def\syst_helpers_split_string ##1#2##2@@@##3##4\\%
+ {\ifx##3\empty\let#3\empty\else\def#3{##2}\fi}%
+ \expandafter\syst_helpers_split_string #1@@@#2@@@\empty\\}
+
+\def\testandsplitstring#1\at#2\to#3\and#4%
+ {\def\syst_helpers_split_string##1#2##2#2##3##4\\%
+ {\ifx##3\empty\let#3\empty\let#4\empty\else\def#3{##1}\def#4{##2}\fi}%
+ \expandafter\syst_helpers_split_string#1#2#2\empty\\}
+
+%D \macros
+%D {removesubstring}
+%D
+%D A first application of the two routines defined above is:
+%D
+%D \starttyping
+%D \removesubstring-\from first-last\to\nothyphenated
+%D \stoptyping
+%D
+%D Which in terms of \TEX\ looks like:
+
+\unexpanded\def\removesubstring#1\from#2\to#3%
+ {\splitstring#2\to\m_syst_string_one\and\m_syst_string_two
+ \dodoglobal#3{\m_syst_string_one\m_syst_string_two}}
+
+%D \macros
+%D {appendtocommalist,prependtocommalist,
+%D addtocommalist,removefromcommalist}
+%D
+%D When working with comma separated lists, one sooner or
+%D later want the tools to append or remove items from such a
+%D list. When we add an item, we first check if it's already
+%D there. This means that every item in the list is unique.
+%D
+%D \starttyping
+%D \addtocommalist {alfa} \name
+%D \addtocommalist {beta} \name
+%D \addtocommalist {gamma} \name
+%D \removefromcommalist {beta} \name
+%D \stoptyping
+%D
+%D These commands can be prefixed with \type{\doglobal}. The
+%D implementation of the second command is more complecated,
+%D because we have to take leading spaces into account. Keep in
+%D mind that users may provide lists with spaces after the
+%D commas. When one item is left, we also have to get rid of
+%D trailing spaces.
+%D
+%D \starttyping
+%D \def\words{alfa, beta, gamma, delta}
+%D \def\words{alfa,beta,gamma,delta}
+%D \stoptyping
+%D
+%D Removing an item takes more time than adding one.
+%D
+%D A fast appending alternative, without any testing, is
+%D also provided:
+%D
+%D \starttyping
+%D \appendtocommalist {something} \name
+%D \prependtocommalist {something} \name
+%D \stoptyping
+%D
+%D This can be implemented as follows:
+%D
+%D \starttyping
+%D \def\appendtocommalist#1#2%
+%D {\ifx#2\empty
+%D \dodoglobal\edef#2{#1}%
+%D \else % no test on empty
+%D \dodoglobal\edef#2{#2,#1}%
+%D \fi}
+%D
+%D \def\prependtocommalist#1#2%
+%D {\ifx#2\empty
+%D \dodoglobal\edef#2{#1}%
+%D \else % no test on empty
+%D \dodoglobal\edef#2{#1,#2}%
+%D \fi}
+%D \stoptyping
+%D
+%D The faster alternatives are:
+
+\unexpanded\def\appendtocommalist#1#2%
+ {\dodoglobal\edef#2{\ifx#2\empty\else#2,\fi#1}}
+
+\unexpanded\def\prependtocommalist#1#2%
+ {\dodoglobal\edef#2{#1\ifx#2\empty\else,#2\fi}}
+
+\unexpanded\def\addtocommalist#1#2% {item} \cs
+ {\rawdoifelseinset{#1}#2\resetglobal
+ {\dodoglobal\edef#2{\ifx#2\empty\else#2,\fi#1}}}
+
+\unexpanded\def\pretocommalist#1#2% {item} \cs
+ {\rawdoifelseinset{#1}#2\resetglobal
+ {\dodoglobal\edef#2{#1\ifx#2\empty\else,#2\fi}}}
+
+\unexpanded\def\robustdoifelseinset#1#2%
+ {\edef\m_syst_string_one{\detokenize\expandafter{\normalexpanded{#1}}}%
+ \edef\m_syst_string_two{\detokenize\expandafter{\normalexpanded{#2}}}%
+ \rawdoifelseinset\m_syst_string_one\m_syst_string_two}
+
+\let\robustdoifinsetelse\robustdoifelseinset
+
+\unexpanded\def\robustaddtocommalist#1#2% {item} \cs
+ {\robustdoifelseinset{#1}#2\resetglobal
+ {\dodoglobal\edef#2{\ifx#2\empty\else#2,\fi#1}}}
+
+\unexpanded\def\robustpretocommalist#1#2% {item} \cs
+ {\robustdoifelseinset{#1}#2\resetglobal
+ {\dodoglobal\edef#2{#1\ifx#2\empty\else,#2\fi}}}
+
+\unexpanded\def\xsplitstring#1#2% \cs {str}
+ {\def\syst_helpers_split_string##1,#2,##2,#2,##3\\%
+ {\edef\m_syst_string_one{\bcleanedupcommalist##1\empty\empty\relax}%
+ \edef\m_syst_string_two{\acleanedupcommalist##2,,\relax}}%
+ \expandafter\syst_helpers_split_string\expandafter,#1,,#2,,#2,\\}
+
+\def\bcleanedupcommalist#1#2#3\relax{\if#1,\else#1\fi\if#2,\else#2\fi#3}
+\def\bcleanedupcommalist#1#2\relax{\if#1,\else#1\fi#2}
+\def\acleanedupcommalist#1,,#2\relax{#1}
+
+\unexpanded\def\removefromcommalist#1#2% to be sped up
+ {\rawdoifelseinset{#1}#2%
+ {\normalexpanded{\xsplitstring\noexpand#2{#1}}%
+ \dodoglobal\edef#2%
+ {\ifx\m_syst_string_one\empty
+ \m_syst_string_two
+ \else
+ \m_syst_string_one\ifx\m_syst_string_two\empty\else,\m_syst_string_two\fi
+ \fi}}
+ \resetglobal}
+
+% \unexpanded\def\addtocommalist#1#2% upto 3 times slower
+% {\dodoglobal\edef#2{\ctxcommand{addtocommalist(\!!bs#1\!!es,\!!bs#2\!!es)}}}
+%
+% \unexpanded\def\removefromcommalist#1#2% faster and more robust
+% {\dodoglobal\edef#2{\ctxcommand{addtocommalist(\!!bs#1\!!es,\!!bs#2\!!es)}}}
+
+%D \macros
+%D {substituteincommalist}
+%D
+%D Slow but seldom used, so for the moment we stick to this
+%D implementation.
+%D
+%D \starttyping
+%D \substituteincommalist{old}{new}{list}
+%D \stoptyping
+
+\def\syst_helpers_substitute_in_comma_list_step#1%
+ {\edef\m_syst_string_three{#1}%
+ \ifx\m_syst_string_one\m_syst_string_three
+ \ifx\m_syst_string_two\empty \else
+ \edef\m_syst_string_four{\ifx\m_syst_string_four\empty\else\m_syst_string_four,\fi\m_syst_string_two}%
+ \fi
+ \else
+ \edef\m_syst_string_four{\ifx\m_syst_string_four\empty\else\m_syst_string_four,\fi#1}%
+ \fi}
+
+\unexpanded\def\substituteincommalist#1#2#3% old, new, list (slooow)
+ {\edef\m_syst_string_one{#1}%
+ \edef\m_syst_string_two{#2}%
+ \let\m_syst_string_four\empty
+ \normalexpanded{\rawprocesscommacommand[#3]}\syst_helpers_substitute_in_comma_list_step
+ \let#3\m_syst_string_four}
+
+%D \macros
+%D {replaceincommalist}
+%D
+%D The next macro can be used to replace an indexed element in a commalist:
+%D
+%D \starttyping
+%D \replaceincommalist\MyList{2}
+%D \stoptyping
+%D
+%D Element~2 will be replaced by the current meaning of the macro \type
+%D {\newcommalistelement}. The old meaning is saved in \type {\commalistelement}.
+%D The replacement honors grouped items, like in:
+%D
+%D \starttyping
+%D \def\MyList{a,b,c,d,e,f} \replaceincommalist\MyList{3}
+%D \def\MyList{a,b,c,d,e,f} \replaceincommalist\MyList{3}
+%D \def\MyList{a,{b,c},d,e,f} \replaceincommalist\MyList{3}
+%D \def\MyList{a,b,c,{d,e,f}} \replaceincommalist\MyList{3}
+%D \stoptyping
+%D
+%D This macro was used in the bibtex code (and is probably no longer needed).
+
+\newcount\c_syst_helpers_comma_list_index
+\let \m_syst_helpers_comma_list_target\empty
+
+\let\newcommalistelement\empty
+
+\def\syst_helpers_replace_in_comma_list_step#1%
+ {\ifnum\commalistcounter=\c_syst_helpers_comma_list_index\relax
+ \ifx\newcommalistelement\empty\else
+ \ifx\m_syst_helpers_comma_list_target\empty
+ \let\m_syst_helpers_comma_list_target\newcommalistelement
+ \else
+ \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter
+ \m_syst_helpers_comma_list_target\expandafter\expandafter\expandafter
+ {\expandafter\m_syst_helpers_comma_list_target\expandafter,\newcommalistelement}%
+ \fi
+ \fi
+ \def\commalistelement{#1}%
+ \else
+ \ifx\m_syst_helpers_comma_list_target\empty
+ \ifx\nexttoken\bgroup % is known -)
+ \def\m_syst_helpers_comma_list_target{{#1}}%
+ \else
+ \def\m_syst_helpers_comma_list_target{#1}%
+ \fi
+ \else
+ \ifx\nexttoken\bgroup % is known -)
+ \expandafter\def\expandafter\m_syst_helpers_comma_list_target\expandafter{\m_syst_helpers_comma_list_target,{#1}}%
+ \else
+ \expandafter\def\expandafter\m_syst_helpers_comma_list_target\expandafter{\m_syst_helpers_comma_list_target,#1}%
+ \fi
+ \fi
+ \fi
+ \advance\commalistcounter\plusone}
+
+\unexpanded\def\replaceincommalist#1#2% #1 = commalistelement #2 = position starts at 1
+ {\c_syst_helpers_comma_list_index#2\relax
+ \let\m_syst_helpers_comma_list_target\empty
+ \let\commalistelement\empty
+ \commalistcounter\plusone
+ \expandafter\processcommalist\expandafter[#1]\syst_helpers_replace_in_comma_list_step
+ \dodoglobal\let#1\m_syst_helpers_comma_list_target}
+
+%D \macros
+%D {globalprocesscommalist}
+%D
+%D The commalist processing commands are characterized by the
+%D fact that the way they handle expansion as well as the fact
+%D that they can be nested. This makes them kind of useless for
+%D handling comma lists in alignments. In these situations the
+%D next macro can be of use.
+
+\let\m_syst_helpers_comma_list_command_global\empty
+
+\def\syst_helpers_comma_list_command_global_step#1,%
+ {\if]#1\else
+ \m_syst_helpers_comma_list_command_global{#1}%
+ \expandafter\syst_helpers_comma_list_command_global_step
+ \fi}
+
+\unexpanded\def\globalprocesscommalist[#1]#2%
+ {\global\let\m_syst_helpers_comma_list_command_global#2%
+ \expandafter\syst_helpers_comma_list_command_global_step#1,],}
+
+%D \macros
+%D {withoutpt,PtToCm,
+%D numberofpoints,dimensiontocount}
+%D
+%D We can convert point into centimeters with:
+%D
+%D \starttyping
+%D \PtToCm{dimension}
+%D \stoptyping
+
+{\catcode`\.=\othercatcode
+ \catcode`\p=\othercatcode
+ \catcode`\t=\othercatcode
+ \gdef\WITHOUTPT#1pt{#1}}
+
+\def\withoutpt#1%
+ {\expandafter\WITHOUTPT#1}
+
+%D The capitals are needed because \type{p} and \type{t} have
+%D \CATCODE~12, while macronames only permit tokens with the
+%D \CATCODE~11. As a result we cannot use the \type{.group}
+%D primitives. Those who want to know more about this kind of
+%D manipulations, we advice to study the \TEX book in detail.
+%D Because this macro does not do any assignment, we can use it
+%D in the following way too.
+
+\def\PtToCm#1%
+ {\withoutpt\the\dimexpr0.0351459804\dimexpr#1\relax\relax cm}
+
+%D We also support:
+%D
+%D \starttyping
+%D \numberofpoints {dimension}
+%D \dimensiontocount {dimension} {\count}
+%D \stoptyping
+%D
+%D Both macros return a rounded number.
+
+% \dimensiontocount{10.49pt}\scratchcounter \the\scratchcounter / \numberofpoints{10.49pt}
+% \dimensiontocount{10.51pt}\scratchcounter \the\scratchcounter / \numberofpoints{10.51pt}
+
+\def\dimensiontocount#1#2{#2\numexpr\dimexpr#1\relax/\maxcard\relax}
+\def\numberofpoints #1{\the\numexpr\dimexpr#1\relax/\maxcard\relax}
+
+%D \macros
+%D {swapdimens,swapcounts,swapmacros,
+%D globalswapdimens,globalswapcounts,globalswapmacros}
+%D
+%D Simple but effective are the next two macros. There name
+%D exactly states their purpose.
+
+\newdimen\d_syst_helpers_swapped
+\newcount\c_syst_helpers_swapped
+\let \m_syst_helpers_swapped\relax
+
+\unexpanded\def\swapdimens#1#2{\d_syst_helpers_swapped #1\relax#1#2\relax#2\d_syst_helpers_swapped}
+\unexpanded\def\swapcounts#1#2{\c_syst_helpers_swapped #1\relax#1#2\relax#2\c_syst_helpers_swapped}
+\unexpanded\def\swapmacros#1#2{\let\m_syst_helpers_swapped#1\let #1#2\let #2\m_syst_helpers_swapped}
+
+\unexpanded\def\globalswapdimens#1#2{\d_syst_helpers_swapped #1\global #1#2\global #2\d_syst_helpers_swapped}
+\unexpanded\def\globalswapcounts#1#2{\c_syst_helpers_swapped #1\global #1#2\global #2\c_syst_helpers_swapped}
+\unexpanded\def\globalswapmacros#1#2{\let\m_syst_helpers_swapped#1\global\let#1#2\global\let#2\m_syst_helpers_swapped}
+
+%D \macros
+%D {pushmacro,popmacro}
+%D
+%D Premature and a bit of beta, we offer:
+%D
+%D \starttyping
+%D \pushmacro\macro
+%D \popmacro\macro
+%D \stoptyping
+%D
+%D Beware: global!
+
+\installsystemnamespace{localpushedmacro}
+\installsystemnamespace{globalpushedmacro}
+
+% \let\m_syst_helpers_push_macro\empty
+%
+% \unexpanded\def\globalpushmacro#1%
+% {\xdef\m_syst_helpers_push_macro{\string#1}%
+% \ifcsname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname \else
+% \expandafter\newcount\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname
+% \fi
+% \global\advance\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname \plusone
+% \global\expandafter\let\csname\the\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname\m_syst_helpers_push_macro\endcsname#1}
+%
+% \unexpanded\def\globalpopmacro#1%
+% {\xdef\m_syst_helpers_push_macro{\string#1}%
+% \global\expandafter\let\expandafter#1\csname\the\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname\m_syst_helpers_push_macro\endcsname
+% \global\advance\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname \minusone}
+%
+% \unexpanded\def\localpushmacro#1% this one can be used to push a value over an \egroup
+% {\xdef\m_syst_helpers_push_macro{\string#1}%
+% \ifcsname\??localpushedmacro\m_syst_helpers_push_macro\endcsname \else
+% \expandafter\newcount\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname
+% \fi
+% \global\advance\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname \plusone
+% \global\expandafter\let\csname\the\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname\m_syst_helpers_push_macro\endcsname#1}
+%
+% \unexpanded\def\localpopmacro#1%
+% {\xdef\m_syst_helpers_push_macro{\string#1}%
+% \expandafter\let\expandafter#1\csname\the\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname\m_syst_helpers_push_macro\endcsname
+% \global\advance\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname \minusone }
+%
+% \let\pushmacro\localpushmacro
+% \let\popmacro \localpopmacro
+%
+% slightly faster but more important: less tracing
+
+\let\m_syst_helpers_push_macro\empty
+
+\newcount\c_syst_helpers_pop_count
+
+\def\syst_helpers_push_macro_new_global
+ {\expandafter\newcount\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname
+ \global\advance\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname\plusone}
+
+\def\syst_helpers_push_macro_new_local
+ {\expandafter\newcount\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname
+ \global\advance\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname\plusone}
+
+\unexpanded\def\globalpushmacro#1%
+ {\xdef\m_syst_helpers_push_macro{\csstring#1}%
+ \ifcsname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname
+ \global\advance\lastnamedcs\plusone
+ \else
+ \syst_helpers_push_macro_new_global
+ \fi
+ \global\expandafter\let\csname\the\lastnamedcs\m_syst_helpers_push_macro\endcsname#1}
+
+\unexpanded\def\localpushmacro#1% this one can be used to push a value over an \egroup
+ {\xdef\m_syst_helpers_push_macro{\csstring#1}%
+ \ifcsname\??localpushedmacro\m_syst_helpers_push_macro\endcsname
+ \global\advance\lastnamedcs\plusone
+ \else
+ \syst_helpers_push_macro_new_local
+ \fi
+ \global\expandafter\let\csname\the\lastnamedcs\m_syst_helpers_push_macro\endcsname#1}
+
+\unexpanded\def\globalpopmacro#1%
+ {\xdef\m_syst_helpers_push_macro{\csstring#1}%
+ \c_syst_helpers_pop_count\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname
+ \global\advance\lastnamedcs \minusone
+ \expandafter\let\expandafter#1\csname\the\c_syst_helpers_pop_count\m_syst_helpers_push_macro\endcsname}
+
+\unexpanded\def\localpopmacro#1%
+ {\xdef\m_syst_helpers_push_macro{\csstring#1}%
+ \c_syst_helpers_pop_count\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname
+ \global\advance\lastnamedcs \minusone
+ \expandafter\let\expandafter#1\csname\the\c_syst_helpers_pop_count\m_syst_helpers_push_macro\endcsname}
+
+\let\pushmacro\localpushmacro
+\let\popmacro \localpopmacro
+
+%D \macros
+%D {setlocalhsize,distributedhsize}
+%D
+%D Sometimes we need to work with the \type{\hsize} that is
+%D corrected for indentation and left and right skips. The
+%D corrected value is available in \type{\localhsize}, which
+%D needs to be calculated with \type{\setlocalhsize} first.
+%D
+%D \starttyping
+%D \setlocalhsize \hbox to \localhsize{...}
+%D \setlocalhsize[-1em] \hbox to \localhsize{...}
+%D \setlocalhsize[.5ex] \hbox to \localhsize{...}
+%D \stoptyping
+%D
+%D These examples show us that an optional can be used. The
+%D value provided is added to \type{\localhsize}.
+
+\newdimen\localhsize
+
+\unexpanded\def\setlocalhsize % don't change !
+ {\doifelsenextoptional
+ \syst_helpers_set_local_hsize_yes
+ \syst_helpers_set_local_hsize_nop}
+
+\def\syst_helpers_set_local_hsize_nop
+ {\localhsize\availablehsize}
+
+\def\syst_helpers_set_local_hsize_yes[#1]%
+ {\syst_helpers_set_local_hsize_nop
+ \advance\localhsize#1\relax}
+
+\def\availablehsize
+ {\dimexpr
+ \hsize-\leftskip-\rightskip
+ \ifnum\hangafter<\zerocount
+ \ifdim\hangindent>\zeropoint-\else+\fi\hangindent
+ \fi
+ \relax}
+
+\def\distributedhsize#1#2#3%
+ {\dimexpr(#1-\numexpr#3-1\relax\dimexpr#2\relax)/#3\relax}
+
+\def\hsizefraction#1#2%
+ {\dimexpr#1/#2\relax}
+
+%D \macros
+%D {doifvalue,doifnotvalue,doifelsevalue,
+%D doifnothing,doifsomething,doifelsenothing,
+%D doifvaluenothing,doifvaluesomething,doifelsevaluenothing}
+%D
+%D These long named \type{\if} commands can be used to access
+%D macros (or variables) that are normally accessed by using
+%D \type{\getvalue}. Using these alternatives safes us three
+%D tokens per call. Anyone familiar with the not||values
+%D ones, can derive their meaning from the definitions.
+
+\unexpanded\def\doifvalue#1#2%
+ {\edef\m_syst_string_one{\csname#1\endcsname}%
+ \edef\m_syst_string_two{#2}%
+ \ifx\m_syst_string_one\m_syst_string_two
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\unexpanded\def\doifnotvalue#1#2%
+ {\edef\m_syst_string_one{\csname#1\endcsname}%
+ \edef\m_syst_string_two{#2}%
+ \ifx\m_syst_string_one\m_syst_string_two
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\unexpanded\def\doifelsevalue#1#2%
+ {\edef\m_syst_string_one{\csname#1\endcsname}%
+ \edef\m_syst_string_two{#2}%
+ \ifx\m_syst_string_one\m_syst_string_two
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\unexpanded\def\doifnothing#1%
+ {\edef\m_syst_string_one{#1}%
+ \ifx\m_syst_string_one\empty
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\unexpanded\def\doifsomething#1%
+ {\edef\m_syst_string_one{#1}%
+ \ifx\m_syst_string_one\empty
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\unexpanded\def\doifelsenothing#1%
+ {\edef\m_syst_string_one{#1}%
+ \ifx\m_syst_string_one\empty
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\unexpanded\def\doifelsesomething#1%
+ {\edef\m_syst_string_one{#1}%
+ \ifx\m_syst_string_one\empty
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+\unexpanded\def\doifvaluenothing#1%
+ {\edef\m_syst_string_one{\csname#1\endcsname}%
+ \ifx\m_syst_string_one\empty
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\unexpanded\def\doifvaluesomething#1%
+ {\edef\m_syst_string_one{\csname#1\endcsname}%
+ \ifx\m_syst_string_one\empty
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\unexpanded\def\doifelsevaluenothing#1%
+ {\edef\m_syst_string_one{\csname#1\endcsname}%
+ \ifx\m_syst_string_one\empty
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\let\doifvalueelse \doifelsevalue
+\let\doifnothingelse \doifelsenothing
+\let\doifsomethingelse \doifelsesomething
+\let\doifvaluenothingelse\doifelsevaluenothing
+
+%D \macros
+%D {doifemptyelsevalue, doifemptyvalue, doifnotemptyvalue}
+%D
+%D Also handy:
+
+\def\doifelseemptyvalue#1%
+ {\expandafter\ifx\csname#1\endcsname\empty
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\let\doifemptyvalueelse\doifelseemptyvalue
+
+\def\doifemptyvalue#1%
+ {\expandafter\ifx\csname#1\endcsname\empty
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\def\doifnotemptyvalue#1%
+ {\expandafter\ifx\csname#1\endcsname\empty
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+%D \macros
+%D {doifallcommonelse}
+%D
+%D A complete match of two sets can be tested with
+%D \type {\doifallcommonelse}, where the first two
+%D arguments are sets.
+
+\def\syst_helpers_do_if_all_common_else#1#2#3#4% slow
+ {\def\syst_helpers_do_common_check_all##1%
+ {\doifnotinset{##1}{#4}\donefalse
+ \ifdone\else\expandafter\quitcommalist\fi}%
+ \donetrue
+ \processcommalist[#3]\syst_helpers_do_common_check_all
+ \ifdone\expandafter#1\else\expandafter#2\fi}
+
+\unexpanded\def\doifelseallcommon{\syst_helpers_do_if_all_common_else\firstoftwoarguments\secondoftwoarguments}
+\unexpanded\def\doifallcommon {\syst_helpers_do_if_all_common_else\firstofonearguments\gobbleoneargument }
+\unexpanded\def\doifnotallcommon {\syst_helpers_do_if_all_common_else\gobbleoneargument \firstofonearguments }
+
+\let\doifallcommonelse\doifelseallcommon
+
+%D \macros
+%D {DOIF,DOIFELSE,DOIFNOT}
+%D
+%D \TEX\ is case sensitive. When comparing arguments, this
+%D feature sometimes is less desirable, for instance when we
+%D compare filenames. The next three alternatives upcase their
+%D arguments before comparing them.
+%D
+%D \starttyping
+%D \DOIF {string1} {string2} {...}
+%D \DOIFNOT {string1} {string2} {...}
+%D \DOIFELSE {string1} {string2} {then ...}{else ...}
+%D \stoptyping
+%D
+%D We have to use a two||step implementation, because the
+%D expansion has to take place outside \type{\uppercase}.
+%D
+%D These might end up as \LUA based helpers (i.e. consider these
+%D obsolete:
+
+\unexpanded\def\syst_helpers_do_IF#1#2%
+ {\uppercase{\syst_helpers_do_if_in_string_else{$#1$}{$#2$}}%
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+\unexpanded\def\syst_helpers_do_IF_NOT#1#2%
+ {\uppercase{\syst_helpers_do_if_in_string_else{$#1$}{$#2$}}%
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\unexpanded\def\syst_helpers_do_IF_ELSE#1#2%
+ {\uppercase{\syst_helpers_do_if_in_string_else{$#1$}{$#2$}}%
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\unexpanded\def\syst_helpers_do_IF_INSTRING_ELSE#1#2%
+ {\uppercase{\syst_helpers_do_if_in_string_else{$#1$}{$#2$}}%
+ \expandafter\firstoftwoarguments
+ \else
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\unexpanded\def\DOIF #1#2{\normalexpanded{\syst_helpers_do_IF {#1}{#2}}}% will become obsolete
+\unexpanded\def\DOIFNOT #1#2{\normalexpanded{\syst_helpers_do_IF_NOT {#1}{#2}}}% will become obsolete
+\unexpanded\def\DOIFELSE #1#2{\normalexpanded{\syst_helpers_do_IF_ELSE {#1}{#2}}}% will become obsolete
+\unexpanded\def\DOIFINSTRINGELSE #1#2{\normalexpanded{\syst_helpers_do_IF_INSTRING_ELSE{#1}{#2}}}% will become obsolete
+
+%D \macros
+%D {dosingleargumentwithset,
+%D dodoubleargumentwithset,dodoubleemptywithset,
+%D dotripleargumentwithset,dotripleemptywithset}
+%D
+%D These maybe too mysterious macros enable us to handle more
+%D than one setup at once.
+%D
+%D \starttyping
+%D \dosingleargumentwithset \command[#1]
+%D \dodoubleargumentwithset \command[#1][#2]
+%D \dotripleargumentwithset \command[#1][#2][#3]
+%D \dodoubleemptywithset \command[#1][#2]
+%D \dotripleemptywithset \command[#1][#2][#3]
+%D \stoptyping
+%D
+%D The first macro calls \type{\command[##1]} for each string
+%D in the set~\type{#1}. The second one calls for
+%D \type{\commando[##1][#2]} and the third, well one may guess.
+%D These commands support constructions like:
+%D
+%D \starttyping
+%D \def\dodefinesomething[#1][#2]%
+%D {\getparameters[\??xx#1][#2]}
+%D
+%D \unexpanded\def\definesomething%
+%D {\dodoubleargumentwithset\dodefinesomething}
+%D \stoptyping
+%D
+%D Which accepts calls like:
+%D
+%D \starttyping
+%D \definesomething[alfa,beta,...][variable=...,...]
+%D \stoptyping
+
+\let\m_syst_helpers_with_set_command\empty
+\let\syst_helpers_with_set_step \relax
+
+\def\syst_helpers_with_set_double[#1][#2]%
+ {\doifsomething{#1}
+ {\def\syst_helpers_with_set_step##1{\m_syst_helpers_with_set_command[##1][#2]}%
+ \processcommalist[#1]\syst_helpers_with_set_step}}
+
+\def\syst_helpers_with_set_triple[#1][#2][#3]%
+ {\doifsomething{#1}
+ {\def\syst_helpers_with_set_step##1{\m_syst_helpers_with_set_command[##1][#2][#3]}%
+ \processcommalist[#1]\syst_helpers_with_set_step}}
+
+\def\dodoubleemptywithset #1{\let\m_syst_helpers_with_set_command#1\dodoubleempty \syst_helpers_with_set_double} % \command
+\def\dodoubleargumentwithset#1{\let\m_syst_helpers_with_set_command#1\dodoubleargument\syst_helpers_with_set_double} % \command
+
+\def\dotripleemptywithset #1{\let\m_syst_helpers_with_set_command#1\dotripleempty \syst_helpers_with_set_triple} % \command
+\def\dotripleargumentwithset#1{\let\m_syst_helpers_with_set_command#1\dotripleargument\syst_helpers_with_set_triple} % \command
+
+%D \macros
+%D {stripcharacters,stripspaces}
+%D
+%D The next command was needed first when we implemented
+%D the \CONTEXT\ interactivity macros. When we use labeled
+%D destinations, we often cannot use all the characters we
+%D want. We therefore strip some of the troublemakers, like
+%D spaces, from the labels before we write them to the
+%D \DVI||file, which passes them to for instance a PostScript
+%D file.
+%D
+%D \starttyping
+%D \stripspaces\from\one\to\two
+%D \stoptyping
+%D
+%D Both the old string \type{\one} and the new one \type{\two}
+%D are expanded. This command is a special case of:
+%D
+%D \starttyping
+%D \stripcharacter\char\from\one\to\two
+%D \stoptyping
+%D
+%D As we can see below, spaces following a control sequence are
+%D to enclosed in \type{{}}.
+
+\let\m_syst_helpers_strip_character\empty
+
+\unexpanded\def\stripcharacter#1\from#2\to#3%
+ {\def\syst_helpers_strip_character##1#1##2\end
+ {\edef\m_syst_helpers_strip_character{\m_syst_helpers_strip_character##1}%
+ \doifnotempty{##2}{\syst_helpers_strip_character##2\end}}%
+ \let\m_syst_helpers_strip_character\empty
+ \edef\m_syst_string_one{#2}%
+ \expandafter\syst_helpers_strip_character\m_syst_string_one#1\end
+ \dodoglobal\let#3\m_syst_helpers_strip_character}
+
+\unexpanded\def\stripspaces\from#1\to#2% will become \unspacestring#1\from#2
+ {\stripcharacter{ }\from#1\to#2}
+
+%D \macros
+%D {unspacestring}
+%D
+%D The next macro does the same but is more compatible with other macros,
+%D like \type {\convert...}.
+
+\unexpanded\def\unspacestring#1\to#2%
+ {\stripcharacter{ }\from#1\to#2}
+
+%D \macros
+%D {executeifdefined}
+%D
+%D \CONTEXT\ uses one auxiliary file for all data concerning
+%D tables of contents, references, two||pass optimizations,
+%D sorted lists etc. This file is loaded as many times as
+%D needed. During such a pass we skip the commands thate are of
+%D no use at that moment. Because we don't want to come into
+%D trouble with undefined auxiliary commands, we call the
+%D macros in a way similar to \type{\getvalue}. The next macro
+%D take care of such executions and when not defined, gobbles
+%D the unwanted arguments.
+%D
+%D \starttyping
+%D \executeifdefined{name}\gobbleoneargument
+%D \stoptyping
+%D
+%D We can of course gobble more arguments using the
+%D appropriate gobbling command.
+
+\def\executeifdefined#1% #2 / never change this one again
+ {\ifcsname#1\endcsname
+ % \csname#1\expandafter\expandafter\expandafter\endcsname\expandafter\gobbleoneargument
+ \expandafter\expandafter\expandafter\lastnamedcs\expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+%D This one also has the advantage that it is fully
+%D expandable and that it can be used after an assignment.
+
+%D \macros
+%D {doifsomespaceelse}
+%D
+%D The next command checks a string on the presence of a space
+%D and executed a command accordingly.
+%D
+%D \starttyping
+%D \doifsomespaceelse {tekst} {then ...} {else ...}
+%D \stoptyping
+%D
+%D We use this command in \CONTEXT\ for determing if an
+%D argument must be broken into words when made interactive.
+%D Watch the use of \type{\noexpand}.
+
+%D Is this one still needed?
+
+\def\syst_helpers_if_some_space_else#1 #2#3\_e_o_s_{\if\noexpand#2@}
+
+\def\doifelsesomespace#1% % #2#3%
+ {\syst_helpers_if_some_space_else#1 @ @\_e_o_s_ % #3\else#2\fi}
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+\let\doifsomespaceelse\doifelsesomespace
+
+%D \macros
+%D {processseparatedlist}
+%D
+%D Maybe a bit late, but here is a more general version of the
+%D \type{\processcommalist} command. This time we don't handle
+%D nesting but accept arbitrary seperators.
+%D
+%D \starttyping
+%D \processseparatedlist[list][separator]\command
+%D \stoptyping
+%D
+%D One can think of things like:
+%D
+%D \starttyping
+%D \processseparatedlist[alfa+beta+gamma][+]\message
+%D \stoptyping
+%D
+%D We want to handle all situations, like:
+%D
+%D \startbuffer
+%D \processseparatedlist[{aap noot}] [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii}
+%D \processseparatedlist[{aap} {noot}][ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii}
+%D \processseparatedlist[aap {noot}] [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii}
+%D \processseparatedlist[aap noot] [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii}
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+%D
+%D Therefore we smuggle a \type {\relax} in front of the
+%D argument, which we remove afterwards.
+
+\let\syst_helpers_process_separated_list_step\relax
+
+\def\syst_helpers_process_separated_list#1]#2[#3]#4%
+ {\def\syst_helpers_process_separated_list_step##1##2#3%
+ {\def\m_syst_string_one{##2}% suggested by VZ
+ \if]##1%
+ \let\syst_helpers_process_separated_list_step\relax
+ \else\ifx\blankspace\m_syst_string_one
+ #4{##1}%
+ \else\if]##2%
+ \let\syst_helpers_process_separated_list_step\relax
+ \else
+ #4{##1##2}%
+ \fi\fi\fi
+ \syst_helpers_process_separated_list_step}%
+ \expandafter\syst_helpers_process_separated_list_step\gobbleoneargument#1#3]#3}
+
+\unexpanded\def\processseparatedlist[%
+ {\syst_helpers_process_separated_list\relax}
+
+%D \macros
+%D {processlist}
+%D
+%D An even more general list processing macro is the following one:
+%D
+%D \starttyping
+%D \processlist{beginsym}{endsym}{separator}\docommand list
+%D \stoptyping
+%D
+%D This one supports arbitrary open and close symbols as well as user
+%D defined separators.
+%D
+%D \starttyping
+%D \processlist(){=>}\docommand(a=>b=>c=>d)
+%D \stoptyping
+
+\let\syst_helpers_process_any_list \relax
+\let\syst_helpers_process_any_list_indeed\relax
+\let\syst_helpers_process_any_list_step \relax
+
+\unexpanded\def\processlist#1#2#3#4% no blank skipping !
+ {\def\syst_helpers_process_any_list_indeed##1#2%
+ {\def\syst_helpers_process_any_list_step####1####2#3%
+ {\ifx#2####1%
+ \let\syst_helpers_process_any_list_step\relax
+ \else\ifx#2####2%
+ \let\syst_helpers_process_any_list_step\relax
+ \else
+ #4{####1####2}%
+ \fi\fi
+ \syst_helpers_process_any_list_step}%
+ \expandafter\syst_helpers_process_any_list_step\gobbleoneargument##1#3#2#3}%
+ \def\syst_helpers_process_any_list#1%
+ {\syst_helpers_process_any_list_indeed\relax}%
+ \syst_helpers_process_any_list}
+
+%D \macros
+%D {processassignlist}
+%D
+%D Is possible to combine an assignment list with one
+%D containing keywords. Assignments are treated accordingly,
+%D keywords are treated by \type{\command}.
+%D
+%D \starttyping
+%D \processassignlist[...=...,...=...,...]\commando
+%D \stoptyping
+%D
+%D This command can be integrated in \type{\getparameters}, but
+%D we decided best not to do so.
+
+\unexpanded\def\processassignlist#1[#2]#3%
+ {\def\syst_helpers_process_assign_list_assign[##1=##2=##3]%
+ {\doifnot{##3}\relax{#3{##1}}}%
+ \def\syst_helpers_process_assign_list_step##1%
+ {\syst_helpers_process_assign_list_assign[##1==\relax]}%
+ \processcommalist[#2]\syst_helpers_process_assign_list_step}
+
+%D \macros
+%D {untextargument
+%D untexcommand}
+%D
+%D When manipulating data(bases) and for instance generating
+%D index entries, the next three macros can be of help:
+%D
+%D \starttyping
+%D \untextargument{...}\to\name
+%D \untexcommand {...}\to\name
+%D \stoptyping
+%D
+%D They remove braces and backslashes and give us something to
+%D sort.
+
+\let\m_syst_helpers_untexed\empty
+
+\unexpanded\def\untexsomething
+ {\begingroup
+ \catcode\leftbraceasciicode \ignorecatcode
+ \catcode\rightbraceasciicode\ignorecatcode
+ \escapechar\minusone
+ \syst_helpers_untex_something}
+
+\def\syst_helpers_untex_something#1#2\to#3%
+ {\doglobal#1#2\to\m_syst_helpers_untexed
+ \endgroup
+ \let#3\m_syst_helpers_untexed}
+
+\unexpanded\def\untexargument{\untexsomething\convertargument}
+\unexpanded\def\untexcommand {\untexsomething\convertcommand}
+
+%D \macros
+%D {ScaledPointsToBigPoints,ScaledPointsToWholeBigPoints}
+%D
+%D One characteristic of \POSTSCRIPT\ and \PDF\ is that both
+%D used big points (\TEX's bp). The next macros convert points
+%D and scaled points into big points.
+%D
+%D \starttyping
+%D \ScaledPointsToBigPoints {number} \target
+%D \ScaledPointsToWholeBigPoints {number} \target
+%D \stoptyping
+%D
+%D The magic factor $72/72.27$ can be found in most \TEX\
+%D related books.
+
+% \PointsToBigPoints{10.53940pt}\test \test
+% \PointsToBigPoints{10.53941pt}\test \test
+% \PointsToBigPoints{10.53942pt}\test \test
+
+% \PointsToWholeBigPoints{10.53940pt}\test \test
+% \PointsToWholeBigPoints{10.53941pt}\test \test
+% \PointsToWholeBigPoints{10.53942pt}\test \test
+
+\unexpanded\def\PointsToBigPoints#1#2%
+ {\edef#2{\withoutpt\the\dimexpr.996264\dimexpr#1\relax\relax}}
+
+\unexpanded\def\PointsToWholeBigPoints#1#2%
+ {\edef#2{\the\numexpr\dimexpr.996264\dimexpr#1\relax\relax/\maxcard\relax}}
+
+\unexpanded\def\ScaledPointsToBigPoints #1{\PointsToBigPoints {\number#1\scaledpoint}}
+\unexpanded\def\ScaledPointsToWholeBigPoints#1{\PointsToWholeBigPoints{\number#1\scaledpoint}}
+
+%D \macros
+%D {PointsToReal}
+%D
+%D Points can be stripped from their suffix by using
+%D \type{\withoutpt}. The next macro enveloppes this macro.
+%D
+%D \starttyping
+%D \PointsToReal {dimension} \target
+%D \stoptyping
+
+\unexpanded\def\PointsToReal#1#2%
+ {\edef#2{\withoutpt\the\dimexpr#1}}
+
+%D \macros
+%D {dontleavehmode}
+%D
+%D Sometimes when we enter a paragraph with some command, the
+%D first token gets the whole first line. We can prevent this
+%D by saying:
+%D
+%D \starttyping
+%D \dontleavehmode
+%D \stoptyping
+%D
+%D This command is used in for instance the language module
+%D \type{lang-ini}. The first version was:
+%D
+%D \starttyping
+%D \def\dontleavehmode{\ifhmode\else\ifmmode\else$ $\fi\fi}
+%D \stoptyping
+%D
+%D Next, Taco came with a better alternative (using mathsurround):
+%D
+%D \starttyping
+%D \def\dontleavehmode
+%D {\ifhmode\else \ifmmode\else
+%D {\mathsurround\zeropoint\everymath\emptytoks$ $}%
+%D \fi \fi}
+%D \stoptyping
+%D
+%D And finaly we got the following alternative, one that avoids
+%D interfering grouping at the cost of a box.
+%D
+%D \starttyping
+%D \newbox\b_syst_helpers_dlh
+%D
+%D \unexpanded\def\dontleavehmode
+%D {\ifhmode\else \ifmmode\else
+%D \setbox\b_syst_helpers_dlh\hbox{\mathsurround\zeropoint\everymath\emptytoks$ $}\unhbox\b_syst_helpers_dlh
+%D \fi \fi}
+%D \stoptyping
+%D
+%D But, as we run a recent version of \TEX, we can use the new primitive:
+
+\ifdefined\normalquitvmode \let\dontleavehmode\normalquitvmode \fi
+
+%D \macros
+%D {utfupper, utflower, uppercasestring, lowercasestring}
+%D
+%D The names tell what they do:
+%D
+%D \starttyping
+%D \uppercasestring somestring\to\somestring
+%D \lowercasestring somestring\to\somestring
+%D \stoptyping
+%D
+%D The first argument may be a \type{\macro}.
+
+% \unexpanded\def\uppercasestring#1\to#2%
+% {\uppercase\expandafter{\expandafter\dodoglobal\expandafter\edef\expandafter#2\expandafter{\normalexpanded{#1}}}}
+%
+% \unexpanded\def\lowercasestring#1\to#2%
+% {\lowercase\expandafter{\expandafter\dodoglobal\expandafter\edef\expandafter#2\expandafter{\normalexpanded{#1}}}}
+
+%D These macros are sort of obsolete as we never use uppercase this
+%D way. But nevertheless we provide them:
+
+\def\utfupper#1{\clf_upper{#1}} % expandable
+\def\utflower#1{\clf_lower{#1}} % expandable
+
+\unexpanded\def\uppercasestring#1\to#2{\dodoglobal\edef#2{\clf_upper{#1}}}
+\unexpanded\def\lowercasestring#1\to#2{\dodoglobal\edef#2{\clf_lower{#1}}}
+
+%D \macros
+%D {handletokens}
+%D
+%D With the next macro we enter a critical area of macro
+%D expansion. What we want is a macro that looks like:
+%D
+%D \starttyping
+%D \handletokens some tokens\with \somemacro
+%D \stoptyping
+%D
+%D A bonus example:
+%D
+%D \starttyping
+%D \hbox{\handletokens tekst en meer tekst\with\ruledhbox}
+%D
+%D \def\weetikveel#1{\if#1\blankspace\space\else\ruledhbox{#1}\fi}
+%D
+%D \hbox{\handletokens tekst en meer tekst\with\weetikveel}
+%D \stoptyping
+
+%D \macros
+%D {counttoken,counttokens}
+%D
+%D For the few occasions that we want to know the number of
+%D specific tokens in a string, we can use:
+%D
+%D \starttyping
+%D \counttoken token\in string\to \somecount
+%D \counttokens string\to \somecount
+%D \stoptyping
+%D
+%D This macro, that for instance is used in \type{cont-tab},
+%D takes a real counter. The macro can be preceded by \type
+%D {\doglobal}.
+
+\def\syst_helpers_count_token#1% obeys {}
+ {\def\m_syst_string_three{#1}%
+ \ifx\m_syst_string_two\m_syst_string_three \else
+ \ifx\m_syst_string_one\m_syst_string_three
+ \advance\scratchcounter\plusone
+ \fi
+ \expandafter\syst_helpers_count_token
+ \fi}
+
+\unexpanded\def\counttoken#1\in#2\to#3%
+ {\scratchcounter\zerocount
+ \def\m_syst_string_one{#1}%
+ \def\m_syst_string_two{\end}%
+ \syst_helpers_count_token#2\end
+ \dodoglobal#3\scratchcounter}
+
+\unexpanded\def\counttokens#1\to#2%
+ {\scratchcounter\zerocount
+ \def\syst_helpers_count_token##1{\advance\scratchcounter\plusone}%
+ \handletokens#1\with\syst_helpers_count_token
+ \dodoglobal#2\scratchcounter}
+
+%D \macros
+%D {splitofftokens}
+%D
+%D Running this one not always gives the expected results.
+%D Consider for instance the macro for which I originally
+%D wrote this token handler.
+
+\unexpanded\def\splitofftokens#1\from#2\to#3% slow but hardly used
+ {\ifnum#1>\zerocount
+ \scratchcounter#1\relax
+ \def\syst_helpers_split_off_tokens##1%
+ {\ifnum\scratchcounter>\zerocount
+ \advance\scratchcounter \minusone
+ \edef#3{#3##1}%
+ \fi}%
+ % \let#3\empty % #3 can be #2, so:
+ \expandafter\let\expandafter#3\expandafter\empty
+ \expandafter\handletokens#2\with\syst_helpers_split_off_tokens
+ \else
+ \edef#3{#2}%
+ \fi}
+
+%D This macro can be called like:
+%D
+%D \startbuffer[example]
+%D \splitofftokens10\from01234567 890123456789\to\test [\test]
+%D \stopbuffer
+%D
+%D However, the characters that we expect to find in
+%D \type{\test} just don't show up there. The reason for this
+%D is not that logical but follows from \TEX's sometimes
+%D mysterious way of expanding. Look at this:
+%D
+%D \startbuffer[next]
+%D \def\next{a} \edef\test{\next} [\test]
+%D \let\next=b \edef\test{\test\next} [\test]
+%D \let\next=c \edef\test{\next} [\test]
+%D \let\next=d \edef\test{\test\next} [\test]
+%D \let\next=e \expandafter\edef\expandafter\test\expandafter{\test\next} [\test]
+%D \stopbuffer
+%D
+%D \typebuffer[next]
+%D
+%D Careful reading shows that inside an \type{\edef} macro's
+%D that are \type{\let} are not expanded!
+%D
+%D \unprotect\getbuffer[next]\protect
+%D
+%D That's why we finally end up with a macro that looks
+%D ahead by using an assignment, this time by using \type
+%D {\futurelet}, and grabbing an argument as well. That
+%D way we can handle the sentinal, a blank space and grouped
+%D tokens.
+
+\unexpanded\def\syst_helpers_handle_tokens % \nexthandledtoken is part of interface
+ {\futurelet\nexthandledtoken\syst_helpers_handle_tokens_indeed}
+
+\def\handletokens#1\with#2%
+ {\gdef\syst_helpers_handle_tokens_command{#2}% permits more complex #2's
+ \syst_helpers_handle_tokens#1\end}
+
+\def\syst_helpers_handle_tokens_indeed
+ {\ifx\nexthandledtoken\blankspace
+ \expandafter\syst_helpers_handle_tokens_indeed_one
+ \else\ifx\nexthandledtoken\end
+ \expandafter\expandafter\expandafter\gobbletwoarguments % also gobble the \end
+ \else
+ \expandafter\expandafter\expandafter\syst_helpers_handle_tokens_indeed_two
+ \fi\fi *}
+
+\def\syst_helpers_handle_tokens_indeed_one * %
+ {\syst_helpers_handle_tokens_command{ }\syst_helpers_handle_tokens}
+
+\def\syst_helpers_handle_tokens_indeed_two *#1%
+ {\syst_helpers_handle_tokens_command{#1}\syst_helpers_handle_tokens}
+
+%D This macro is tested on:
+%D
+%D \def\xxx#1{[#1]}
+%D
+%D \startlines
+%D \handletokens abc\with\xxx
+%D \handletokens a b c\with\xxx
+%D \handletokens a b c\with\xxx
+%D \handletokens a{bc}d\with\xxx
+%D \handletokens a\space bc \with\xxx
+%D \stoplines
+%D
+%D And our previous example shows up as:
+%D
+%D \getbuffer[example]
+
+%D \macros
+%D {iftrialtypesetting, ifvisible}
+%D
+%D The next boolean is at first sight a strange one. Sometimes
+%D one does a trial typesetting run, for instance to determine
+%D dimensions. Some mechanisms, like object inclusion, can fail
+%D on such trials. Temporary setting the next boolean to true,
+%D helps a lot. The second boolena can be used to inhibit
+%D processing completely.
+
+\newif\ifvisible \visibletrue
+
+\newtoks\everysettrialtypesetting
+\newtoks\everyresettrialtypesetting
+
+\unexpanded\def\settrialtypesetting {\the\everysettrialtypesetting } % obeys grouping so
+\unexpanded\def\resettrialtypesetting{\the\everyresettrialtypesetting} % this one is seldom needed
+
+\let\iftrialtypesetting\iffalse % so we have no \trialtypesettingtrue|false in mkiv !
+
+\appendtoks \let\iftrialtypesetting\iftrue \to \everysettrialtypesetting
+\appendtoks \let\iftrialtypesetting\iffalse \to \everyresettrialtypesetting
+
+%D \macros
+%D {twodigitrounding}
+%D
+%D When using \type {\special}s or \type {\pdfliteral}s, it
+%D sometimes makes sense to limit the precission. The next
+%D macro rounds a real number to two digits. It takes one
+%D argument and only works in \ETEX.
+
+% \def\dointegerrounding #1.#2\relax {#1}
+% \def\doonedigitrounding #1.#2#3\relax {\ifx#2*#1\else#1.#2\fi}
+% \def\dotwodigitrounding #1.#2#3#4\relax {\ifx#2*#1\else#1.#2#3\fi}
+% \def\dothreedigitrounding#1.#2#3#4#5\relax{\ifx#2*#1\else#1.#2#3#4\fi}
+%
+% \def\integerrounding#1%
+% {\expandafter\expandafter\expandafter\dointegerrounding \expandafter\WITHOUTPT\the\dimexpr#1\points+.5\points \relax .\relax}
+% \def\onedigitrounding#1%
+% {\expandafter\expandafter\expandafter\doonedigitrounding \expandafter\WITHOUTPT\the\dimexpr#1\points+.05\points \relax 00.*0\relax}
+% \def\twodigitrounding#1%
+% {\expandafter\expandafter\expandafter\dotwodigitrounding \expandafter\WITHOUTPT\the\dimexpr#1\points+.005\points \relax 000.*00\relax}
+% \def\threedigitrounding#1%
+% {\expandafter\expandafter\expandafter\dothreedigitrounding\expandafter\WITHOUTPT\the\dimexpr#1\points+.0005\points\relax0000.*00\relax}
+
+\def\integerrounding #1{\clf_rounded\zerocount\numexpr#1\relax}
+\def\onedigitrounding #1{\clf_rounded\plusone \numexpr#1\relax}
+\def\twodigitrounding #1{\clf_rounded\plustwo \numexpr#1\relax}
+\def\threedigitrounding#1{\clf_rounded\plusthree\numexpr#1\relax}
+
+%D \macros
+%D {processcontent}
+%D
+%D This macro is first used in the tabulation macros.
+%D
+%D \starttyping
+%D \unexpanded\def\starthans%
+%D {\processcontent{stophans}\test{\message{\test}\wait}}
+%D \stoptyping
+
+\unexpanded\def\processcontent#1%
+ {\begingroup\expandafter\syst_helpers_process_content\csname#1\endcsname}
+
+\unexpanded\def\syst_helpers_process_content#1#2#3%
+ {\unexpanded\def\syst_helpers_process_content##1#1%
+ {\endgroup\def#2{##1}#3}%
+ \syst_helpers_process_content}
+
+%D \macros
+%D {dogobblesingleempty, dogobbledoubleempty}
+%D
+%D These two macros savely grab and dispose two arguments.
+
+\def\dogobblesingleempty{\dosingleempty\syst_helpers_gobble_single_empty}
+\def\dogobbledoubleempty{\dodoubleempty\syst_helpers_gobble_double_empty}
+
+\def\syst_helpers_gobble_single_empty [#1]{}
+\def\syst_helpers_gobble_double_empty[#1][#2]{}
+
+\let\gobblesingleempty\dogobblesingleempty % also used
+\let\gobbledoubleempty\dogobbledoubleempty % also used
+
+%D \macros
+%D {@True, @False, @Not, @And}
+%D
+%D Some predicate logic functions, used in for instance the
+%D math module.
+
+% These have rather ugly names ... will change:
+
+\def\@True {00}
+\def\@False {01}
+\def\@Not #1{0\ifcase#11 \or\expandafter 1\else \expandafter 0\fi}
+\def\@And #1#2{0\ifcase#1#2 \expandafter 0\else \expandafter 1\fi}
+
+%D \macros
+%D {setdimensionwithunit, freezedimensionwithunit}
+%D
+%D The next assignments are all valid:
+%D
+%D \starttyping
+%D \setdimensionwithunit\scratchdimen{10} {cm}
+%D \setdimensionwithunit\scratchdimen{10cm}{cm}
+%D \setdimensionwithunit\scratchdimen{10cm}{}
+%D \freezedimensionwithunit\SomeWidth{\textwidth}
+%D \freezedimensionwithunit\SomeDepth{\dp\strutbox}
+%D \stoptyping
+%D
+%D As an alternative for the next macro we can use a global
+%D assignment inside a box. The \type{\empty}'s permits
+%D gobbling while preventing spurious \type{\relax}'s.
+
+\unexpanded\def\setdimensionwithunit#1#2#3% number unit dimension / nice trick
+ {\afterassignment\gobblefourarguments#1=#2#3pt\relax\empty\empty\empty\empty}
+
+\unexpanded\def\freezedimensionwithunit#1#2%
+ {\setdimensionwithunit\scratchdimen#1{#2}\edef#1{\the\scratchdimen}}
+
+%D \macros
+%D {doifsometokselse, doifsometoks}
+%D
+%D Not that fast I guess, but here's a way to test for token
+%D registers being empty.
+
+\unexpanded\def\doifelsesometoks#1%
+ {\edef\m_syst_string_one{\the#1}% one level expansion so quite ok
+ \ifx\m_syst_string_one\empty
+ \expandafter\secondoftwoarguments
+ \else
+ \expandafter\firstoftwoarguments
+ \fi}
+
+\let\doifsometokselse\doifelsesometoks
+
+\unexpanded\def\doifsometoks#1%
+ {\edef\m_syst_string_one{\the#1}% one level expansion so quite ok
+ \ifx\m_syst_string_one\empty
+ \expandafter\gobbleoneargument
+ \else
+ \expandafter\firstofoneargument
+ \fi}
+
+\unexpanded\def\doifemptytoks#1%
+ {\edef\m_syst_string_one{\the#1}% one level expansion so quite ok
+ \ifx\m_syst_string_one\empty
+ \expandafter\firstofoneargument
+ \else
+ \expandafter\gobbleoneargument
+ \fi}
+
+%D \macros
+%D {startstrictinspectnextcharacter}
+%D
+%D This one if for Taco's bibliography module:
+
+\let\syst_helpers_normal_inspect_next_character\syst_helpers_inspect_next_character
+
+\def\syst_helpers_strict_inspect_next_character% no user macro !
+ {\ifx\nexttoken\charactertoken
+ \expandafter\m_syst_action_yes
+ \else
+ \expandafter\m_syst_action_nop
+ \fi}
+
+% better: push/pop
+
+\unexpanded\def\startstrictinspectnextcharacter
+ {\let\syst_helpers_inspect_next_character\syst_helpers_strict_inspect_next_character}
+
+\unexpanded\def\stopstrictinspectnextcharacter
+ {\let\syst_helpers_inspect_next_character\syst_helpers_normal_inspect_next_character}
+
+\unexpanded\def\strictdoifelsenextoptional#1#2%
+ {\startstrictinspectnextcharacter
+ \doifelsenextchar[{\stopstrictinspectnextcharacter#1}{\stopstrictinspectnextcharacter#2}}
+
+\let\strictdoifnextoptionalelse\strictdoifelsenextoptional
+
+%D \macros
+%D {gobblespacetokens}
+%D
+%D This macro needs a speed-up!
+
+%\def\gobblespacetokens
+% {\doifnextcharelse\empty\donothing\donothing} % no {}\do\do !
+
+\def\gobblespacetokens
+ {\afterassignment\nexttoken\let\nexttoken=}
+
+%D \macros
+%D {verbatimargument}
+%D
+%D As the name says, this macro converts its argument to a
+%D (rather safe) string.
+
+\let\verbatimstring\detokenize
+
+%D These are needed in ordinal number conversions:
+
+\def\lastdigit#1%
+ {\expandafter\thelastdigit\number#1\relax}
+
+\def\thelastdigit#1#2%
+ {\ifx#2\relax#1\else\expandafter\thelastdigit\expandafter#2\fi}
+
+\def\lasttwodigits#1%
+ {\expandafter\thelasttwodigits\expandafter0\number#1\relax}
+
+\def\thelasttwodigits#1#2#3% 0 dig ... \relax
+ {\ifx#3\relax#1#2\else\expandafter\thelasttwodigits\expandafter#2\expandafter#3\fi}
+
+%D \macros
+%D {serializecommalist}
+%D
+%D Concatenate commalists:
+
+\let\syst_helpers_serialize_comma_list_step\relax
+
+\def\syst_helpers_serialize_comma_list_step#1%
+ {\edef\serializedcommalist{\serializedcommalist#1}}
+
+\unexpanded\def\serializecommalist[#1]%
+ {\let\serializedcommalist\empty
+ \processcommacommand[#1]\syst_helpers_serialize_comma_list_step}
+
+%D \macros
+%D {purenumber}
+%D
+%D Sometimes we need control over when \TEX\ stops reading a
+%D number, especially in full expandable macros where using
+%D \type {\relax} would lead to disasters.
+%D
+%D \starttyping
+%D \ifodd\purenumber{...}\space ... \else ... \fi
+%D \stoptyping
+%D
+%D Here we use a space as number delimiter in combination
+%D with a space- and relax-less \type {\purenumber}. This
+%D macro works ok with \type {\the}, \type {\number} as well
+%D as \ETEX's \type {\numexpr}.
+
+\def\purenumber#1{\expandafter\firstofoneargument\expandafter{\number#1}}
+
+%D \macros
+%D {filterfromvalue}
+%D
+%D \starttyping
+%D \setvalue{xx}{{A}{B}{C}}
+%D
+%D \filterfromvalue{xx}{3}{3}
+%D \filterfromvalue{xx}{3}{2}
+%D \filterfromvalue{xx}{3}{1}
+%D \stoptyping
+%D
+%D An alternative is to store 'max' in the list, say:
+%D
+%D \starttyping
+%D \setvalue{xx}{3{A}{B}{C}}
+%D
+%D \filterfromvalues{3}{xx}{3}
+%D \filterfromvalues{3}{xx}{2}
+%D \filterfromvalues{3}{xx}{1}
+%D \stoptyping
+%D
+%D I'll implement this when I'm in \quotation {writing dirty
+%D macros mood}.
+
+\def\dofilterfromstr#1#2% max n % no need to be fast
+ {\expandafter \expandafter \expandafter \csstring
+ \ifcase#1\or \ifcase#2\or
+ \firstofoneargument \else
+ \gobbleoneargument \fi
+ \or \ifcase#2\or
+ \firstoftwoarguments \or
+ \secondoftwoarguments \else
+ \gobbletwoarguments \fi
+ \or \ifcase#2\or
+ \firstofthreearguments \or
+ \secondofthreearguments \or
+ \thirdofthreearguments \else
+ \gobblethreearguments \fi
+ \or \ifcase#2\or
+ \firstoffourarguments \or
+ \secondoffourarguments \or
+ \thirdoffourarguments \or
+ \fourthoffourarguments \else
+ \gobblefourarguments \fi
+ \or \ifcase#2\or
+ \firstoffivearguments \or
+ \secondoffivearguments \or
+ \thirdoffivearguments \or
+ \fourthoffivearguments \or
+ \fifthoffivearguments \else
+ \gobblefivearguments \fi
+ \fi}
+
+\def\filterfromvalue#1#2#3% value max n
+ {\expandafter\doubleexpandafter\csname % we use the fact that an
+ \expandafter\ifx\csname#1\endcsname\relax % undefined cs has become \relax
+ \csstring\gobbleoneargument % which we then gobble here
+ \else
+ \dofilterfromstr{#2}{#3}%
+ \fi
+ \endcsname\csname#1\endcsname}
+
+\def\filterfromnext#1#2% max n {..}{..}{..}{..}
+ {\csname\dofilterfromstr{#1}{#2}\endcsname}
+
+%D \macros
+%D {definemeasure}
+%D
+%D \starttyping
+%D \definemeasure[mywidth][\dimexpr(\textwidth-1cm)]
+%D
+%D ... \measure{mywidth} ...
+%D \stoptyping
+
+\installsystemnamespace{measure}
+
+\unexpanded\def\definemeasure
+ {\dodoubleargument\syst_helpers_define_measure}
+
+\def\syst_helpers_define_measure[#1][#2]%
+ {\expandafter\def\csname\??measure#1\endcsname{#2}}
+
+\unexpanded\def\freezemeasure
+ {\dodoubleargument\syst_helpers_freeze_measure}
+
+\def\syst_helpers_freeze_measure[#1][#2]%
+ {\expandafter\edef\csname\??measure#1\endcsname{\the\dimexpr#2}}
+
+\unexpanded\def\setmeasure #1#2{\expandafter\def \csname\??measure#1\endcsname{#2}} % quick way
+\unexpanded\def\setgmeasure#1#2{\expandafter\gdef\csname\??measure#1\endcsname{#2}} % quick way
+\unexpanded\def\setemeasure#1#2{\expandafter\edef\csname\??measure#1\endcsname{\the\dimexpr#2}} % quick way
+\unexpanded\def\setxmeasure#1#2{\expandafter\xdef\csname\??measure#1\endcsname{\the\dimexpr#2}} % quick way
+
+\def\measure
+ {\the\measured}
+
+\def\measured#1%
+ %{\dimexpr\ifcsname\??measure#1\endcsname\csname\??measure#1\endcsname\else\zeropoint\fi\relax}
+ {\dimexpr\ifcsname\??measure#1\endcsname\lastnamedcs\else\zeropoint\fi\relax}
+
+% #2 could be omitted, but we want to support spaces
+%
+% \setmeasure {x} {1cm}
+% \setmeasure {xx} {1cm}
+% \setmeasure {xxx}{1cm}
+
+%D \macros
+%D {dividedsize}
+%D
+%D This one can be used inside a measure (used in m4all):
+%D
+%D \starttyping
+%D \definemeasure[columnwidth][\dividedsize\textwidth{1em}{3}]
+%D \stoptyping
+
+\def\dividedsize#1#2#3% size gap n
+ {\dimexpr
+ \ifnum\dimexpr#1\relax>\plusone
+ (\dimexpr#1\relax-\numexpr#3-\plusone\relax\dimexpr#2\relax)/#3\else#1%
+ \fi
+ \relax}
+
+%D \macros
+%D {doifdimensionelse}
+%D
+%D This is a dirty one: we simply append a unit and discard it when needed.
+
+\def\doifelsedimension#1%
+ {\afterassignment\syst_helpers_if_dimension_else\scratchdimen#1pt\relax}
+
+\let\doifdimensionelse\doifelsedimension
+
+\def\syst_helpers_if_dimension_else#1%
+ {\ifx#1\relax
+ \expandafter\secondoftwoarguments
+ \else % #1=p ... t\relax
+ \expandafter\thirdoffourarguments
+ \fi}
+
+%D Ok, here's another one, slower but seldom used. This one scans the text.
+%D
+%D \starttabulate[|Tc|Tc|]
+%D \NC pt \NC \doifdimenstringelse {pt}{yes}{no} \NC \NR
+%D \NC 12pt \NC \doifdimenstringelse {-12pt}{yes}{no} \NC \NR
+%D \NC 1pt \NC \doifdimenstringelse {1pt}{yes}{no} \NC \NR
+%D \NC 12pt \NC \doifdimenstringelse {12pt}{yes}{no} \NC \NR
+%D \NC 12.0pt \NC \doifdimenstringelse {12.0pt}{yes}{no} \NC \NR
+%D \NC -.12pt \NC \doifdimenstringelse {-.12pt}{yes}{no} \NC \NR
+%D \NC .12pt \NC \doifdimenstringelse {.12pt}{yes}{no} \NC \NR
+%D \NC -12pt \NC \doifdimenstringelse {-12pt}{yes}{no} \NC \NR
+%D \NC -12.0pt \NC \doifdimenstringelse{-12.0pt}{yes}{no} \NC \NR
+%D \NC big \NC \doifdimenstringelse {big}{yes}{no} \NC \NR
+%D \NC 10 \NC \doifdimenstringelse {10}{yes}{no} \NC \NR
+%D \NC 1 \NC \doifdimenstringelse {1}{yes}{no} \NC \NR
+%D \stoptabulate
+
+\installsystemnamespace{dimenchecka}
+\installsystemnamespace{dimencheckb}
+\installsystemnamespace{dimencheckc}
+
+\def\doifelsedimenstring#1{\normalexpanded{\noexpand\dodimenteststageone#1}\empty\empty]}
+
+\let\doifdimenstringelse\doifelsedimenstring
+
+\def\dodimenteststageone #1#2{\csname \??dimenchecka\ifcsname \??dimenchecka#2\endcsname#2\else x\fi\endcsname#2}
+\def\dodimenteststagetwo #1#2{\csname \??dimencheckb\ifcsname \??dimencheckb#2\endcsname#2\else x\fi\endcsname#2}
+\def\dodimenteststagethree #1]{\csname \??dimencheckc\ifcsname \??dimencheckc#1\endcsname#1\else x\fi\endcsname}
+
+\expandafter\let\csname \??dimenchecka x\endcsname\dodimenteststagethree
+\expandafter\let\csname \??dimencheckb x\endcsname\dodimenteststagethree
+\expandafter\let\csname \??dimencheckc x\endcsname\secondoftwoarguments
+
+\expandafter\let\csname \??dimenchecka.\endcsname\dodimenteststagetwo
+\expandafter\let\csname \??dimenchecka,\endcsname\dodimenteststagetwo
+\expandafter\let\csname \??dimenchecka1\endcsname\dodimenteststageone
+\expandafter\let\csname \??dimenchecka2\endcsname\dodimenteststageone
+\expandafter\let\csname \??dimenchecka3\endcsname\dodimenteststageone
+\expandafter\let\csname \??dimenchecka4\endcsname\dodimenteststageone
+\expandafter\let\csname \??dimenchecka5\endcsname\dodimenteststageone
+\expandafter\let\csname \??dimenchecka6\endcsname\dodimenteststageone
+\expandafter\let\csname \??dimenchecka7\endcsname\dodimenteststageone
+\expandafter\let\csname \??dimenchecka8\endcsname\dodimenteststageone
+\expandafter\let\csname \??dimenchecka9\endcsname\dodimenteststageone
+\expandafter\let\csname \??dimenchecka0\endcsname\dodimenteststageone
+
+\expandafter\let\csname \??dimencheckb1\endcsname\dodimenteststagetwo
+\expandafter\let\csname \??dimencheckb2\endcsname\dodimenteststagetwo
+\expandafter\let\csname \??dimencheckb3\endcsname\dodimenteststagetwo
+\expandafter\let\csname \??dimencheckb4\endcsname\dodimenteststagetwo
+\expandafter\let\csname \??dimencheckb5\endcsname\dodimenteststagetwo
+\expandafter\let\csname \??dimencheckb6\endcsname\dodimenteststagetwo
+\expandafter\let\csname \??dimencheckb7\endcsname\dodimenteststagetwo
+\expandafter\let\csname \??dimencheckb8\endcsname\dodimenteststagetwo
+\expandafter\let\csname \??dimencheckb9\endcsname\dodimenteststagetwo
+\expandafter\let\csname \??dimencheckb0\endcsname\dodimenteststagetwo
+
+\expandafter\let\csname \??dimencheckc pt\endcsname\firstoftwoarguments
+\expandafter\let\csname \??dimencheckc pc\endcsname\firstoftwoarguments
+\expandafter\let\csname \??dimencheckc in\endcsname\firstoftwoarguments
+\expandafter\let\csname \??dimencheckc bp\endcsname\firstoftwoarguments
+\expandafter\let\csname \??dimencheckc cm\endcsname\firstoftwoarguments
+\expandafter\let\csname \??dimencheckc mm\endcsname\firstoftwoarguments
+\expandafter\let\csname \??dimencheckc dd\endcsname\firstoftwoarguments
+\expandafter\let\csname \??dimencheckc cc\endcsname\firstoftwoarguments
+\expandafter\let\csname \??dimencheckc sp\endcsname\firstoftwoarguments
+\expandafter\let\csname \??dimencheckc ex\endcsname\firstoftwoarguments
+\expandafter\let\csname \??dimencheckc em\endcsname\firstoftwoarguments
+\expandafter\let\csname \??dimencheckc nd\endcsname\firstoftwoarguments
+\expandafter\let\csname \??dimencheckc nc\endcsname\firstoftwoarguments
+
+%D \macros
+%D {comparedimension,comparedimensioneps}
+%D
+%D This is a dirty one: we simply append a unit and discard it when needed.
+
+\newdimen \roundingeps \roundingeps=10sp
+\newconstant\compresult
+
+\def\comparedimension#1#2%
+ {\compresult
+ \ifdim#1<#2%
+ \zerocount
+ \else\ifdim#1<#2%
+ \plusone
+ \else
+ \plustwo
+ \fi\fi}
+
+\def\comparedimensioneps#1#2% todo: use eps feature
+ {\compresult
+ \ifdim\dimexpr#1-#2\relax<\roudingeps
+ \zerocount
+ \else\ifdim\dimexpr#2-#1\relax<\roudingeps
+ \zerocount
+ \else\ifdim#1<#2%
+ \plusone
+ \else
+ \plustwo
+ \fi\fi\fi}
+
+% % % % % % % % % % % % % % % % % % % % % %
+
+% pretty ugly but fast
+
+% \copycsname xxx\endcsname\csname ..\endcsname
+
+\unexpanded\def\copycsname{\expandafter\expandafter\expandafter\let\expandafter\expandafter\csname}
+
+% \letcscsname \crap \csname ..\endcsname
+% \letcsnamecs \csname ..\endcsname\crap
+% \letcsnamecsname\csname ..\endcsname\csname ..\endcsname
+
+\unexpanded\def\letcscsname {\expandafter\let\expandafter}
+\unexpanded\def\letcsnamecs {\expandafter\let}
+\unexpanded\def\letcsnamecsname{\expandafter\expandafter\expandafter\let\expandafter\expandafter}
+
+% another one, add an item to a commalist
+
+\unexpanded\def\addvalue#1#2% cs item
+ {\ifcsname#1\endcsname\else\expandafter\let\csname#1\endcsname\empty\fi
+ \normalexpanded{\noexpand\addtocommalist{#2}\expandafter\noexpand\csname#1\endcsname}}
+
+\def\unspaced#1%
+ {\syst_helpers_unspaced#1\end}
+
+\def\syst_helpers_unspaced#1%
+ {\ifx#1\end
+ \expandafter\gobbleoneargument
+ \else
+ \ifx#1\blankspace\else#1\fi
+ \fi
+ \syst_helpers_unspaced}
+
+\unexpanded\def\unspaceargument#1\to#2%
+ {\scratchcounter\catcode\spaceasciicode
+ \catcode\spaceasciicode\ignorecatcode
+ \scantextokens{\edef#2{#1}}%
+ \catcode\spaceasciicode\scratchcounter}
+
+\unexpanded\def\unspaceafter#1#2%
+ {\unspaceargument#2\to\ascii
+ \expandafter#1\expandafter{\ascii}}
+
+% sometimes handy:
+
+\unexpanded\def\doifelsehasspace#1%
+ {\edef\m_syst_string_one{#1}%
+ \normalexpanded{\syst_helpers_if_has_space_else#1\space}\empty\relax}
+
+\let\doifhasspaceelse\doifelsehasspace
+
+\unexpanded\def\syst_helpers_if_has_space_else#1 #2#3\relax % \space\empty\relax
+ {\ifx\m_syst_string_one\space
+ \expandafter\firstoftwoarguments
+ \else\ifx#2\empty
+ \doubleexpandafter\secondoftwoarguments
+ \else
+ \doubleexpandafter\firstoftwoarguments
+ \fi\fi}
+
+% this will replace loadfile once and alike !!! todo
+
+\installsystemnamespace{flag}
+
+\unexpanded\def\setflag #1{\expandafter\dodoglobal\expandafter\let\csname\??flag#1\endcsname\zerocount}
+\unexpanded\def\resetflag#1{\expandafter\dodoglobal\expandafter\let\csname\??flag#1\endcsname\plusone}
+
+\def\flag#1{\csname\??flag#1\endcsname}
+
+\def\doifelseflagged#1%
+ {\expandafter\ifx\csname\??flag#1\endcsname\relax
+ \expandafter\secondoftwoarguments
+ \else\ifcase\csname\??flag#1\endcsname
+ \doubleexpandafter\firstoftwoarguments
+ \else
+ \doubleexpandafter\secondoftwoarguments
+ \fi\fi}
+
+\let\doifflaggedelse\doifelseflagged
+
+\def\doifnotflagged#1%
+ {\expandafter\ifx\csname\??flag#1\endcsname\relax
+ \expandafter\firstofoneargument
+ \else\ifcase\csname\??flag#1\endcsname
+ \doubleexpandafter\gobbleoneargument
+ \else
+ \doubleexpandafter\firstofoneargument
+ \fi\fi}
+
+\unexpanded\def\inheritparameter[#1]#2[#3]#4[#5]% tag tokey fromkey
+ {\expandafter\def\csname#1#3\expandafter\endcsname\expandafter{\csname#1#5\endcsname}}
+
+\def\syst_helpers_if_non_zero_positive_else#1#2\end % #3#4%
+ {\ifx#1\relax
+ \ifcase\scratchcounter
+ \endgroup
+ \doubleexpandafter\secondoftwoarguments
+ \else
+ \endgroup
+ \doubleexpandafter\firstoftwoarguments
+ \fi
+ \else
+ \endgroup
+ \expandafter\secondoftwoarguments
+ \fi}
+
+\def\doifelsenonzeropositive#1%
+ {\begingroup\afterassignment\syst_helpers_if_non_zero_positive_else\scratchcounter=0#1\relax\empty\end}
+
+\let\doifnonzeropositiveelse\doifelsenonzeropositive
+
+% here ?
+
+\unexpanded\def\dosetrawvalue #1#2#3{\expandafter \def\csname#1#2\endcsname{#3}}
+\unexpanded\def\dosetrawevalue#1#2#3{\expandafter\edef\csname#1#2\endcsname{#3}}
+\unexpanded\def\dosetrawgvalue#1#2#3{\expandafter\gdef\csname#1#2\endcsname{#3}}
+\unexpanded\def\dosetrawxvalue#1#2#3{\expandafter\xdef\csname#1#2\endcsname{#3}}
+
+\unexpanded\def\getrawparameters {\dogetparameters\dosetrawvalue }
+\unexpanded\def\getraweparameters {\dogetparameters\dosetrawevalue}
+\unexpanded\def\getrawgparameters {\dogetparameters\dosetrawgvalue}
+\unexpanded\def\getrawxparameters {\dogetparameters\dosetrawxvalue}
+
+\unexpanded\def\globalgetrawparameters{\dogetparameters\dosetrawgvalue} % obsolete
+
+%D Sort of obsolete:
+
+\newcount\c_syst_helpers_mod
+
+\unexpanded\def\dosetmodulo#1#2#3%
+ {\c_syst_helpers_mod#1\divide\c_syst_helpers_mod#2\multiply\c_syst_helpers_mod#2%
+ #3#1\advance#3-\c_syst_helpers_mod}
+
+\unexpanded\def\dosetdivision#1#2#3%
+ {#3#1\divide#3 #2\relax}
+
+\unexpanded\def\DoMod#1by#2to#3{\dosetmodulo {#1}{#2}{#3}}
+\unexpanded\def\DoDiv#1by#2to#3{\dosetdivision{#1}{#2}{#3}}
+
+\def\syst_helpers_unprotected#1\par
+ {#1\protect}
+
+\unexpanded\def\unprotected
+ {\unprotect
+ \syst_helpers_unprotected}
+
+% awaiting the definitive implementation
+
+% \ifdefined\resettimer \else
+% \let\resettimer \relax
+% \newcount\elapsedtime
+% \fi
+% \def\elapsedseconds{\expandafter\withoutpt\the\dimexpr\elapsedtime sp\relax}
+
+\let\resettimer \clf_resettimer
+\let\elapsedtime \clf_elapsedtime
+\let\elapsedseconds \elapsedtime
+
+\newcount\c_syst_helpers_test_feature_n
+
+\unexpanded\def\testfeature#1#2%
+ {\def\syst_helpers_test_feature_step
+ {\advance\c_syst_helpers_test_feature_n\plusone
+ \ifnum\c_syst_helpers_test_feature_n>#1\else#2\expandafter\syst_helpers_test_feature_step\fi}%
+ \retestfeature}
+
+\unexpanded\def\retestfeature % timer support is new per 10/5/2005
+ {\bgroup
+ \ifcase\interactionmode\let\wait\relax\fi
+ \writestatus\m!system{starting feature test}\wait
+ \resettimer
+ \c_syst_helpers_test_feature_n\zerocount
+ \syst_helpers_test_feature_step
+ \writestatus\m!system{feature test done (\elapsedseconds s)}%
+ \wait
+ \egroup}
+
+\unexpanded\def\showtimer#1%
+ {\writestatus{runtime}{\elapsedseconds\space s / #1}}
+
+\unexpanded\def\testfeatureonce#1#2%
+ {\begingroup
+ \let\wait\relax
+ \testfeature{#1}{#2}%
+ \endgroup}
+
+%D \macros
+%D {freezedimenmacro}
+%D
+%D This macro is use as:
+%D
+%D \starttyping
+%D \freezedimenmacro\leftmargindistance
+%D \stoptyping
+
+\unexpanded\def\freezedimenmacro#1%
+ {\edef#1{\the\dimexpr#1}}
+
+%D The next macro negates a macro (dimension or number, or actually, whatever.
+%D It's a typical example of \type {\if} usage:
+%D
+%D \starttyping
+%D \if-\whatever \else-\whatever\fi => else => -whatever
+%D \if--\whatever\else-\whatever\fi => then => whatever
+%D \stoptyping
+
+\def\negated#1{\if-#1\else-#1\fi} % does only work in macros or text
+
+\def\gobbleassigndimen#1\\{}
+
+\def\assigndimen#1#2%
+ {\afterassignment\gobbleassigndimen#1=#2\zeropoint\\}
+
+\unexpanded\def\appended#1#2#3{\expandafter#1\expandafter#2\expandafter{#2#3}}
+\unexpanded\def\appendvalue #1{\expandafter\appended\expandafter \def\csname#1\endcsname}
+\unexpanded\def\appendgvalue#1{\expandafter\appended\expandafter\gdef\csname#1\endcsname}
+
+\unexpanded\def\prepended#1#2#3%
+ {\t_syst_helpers_scratch{#3}%
+ \expandafter\expandafter\expandafter#1\expandafter\expandafter\expandafter#2\expandafter\expandafter\expandafter
+ {\expandafter\the\expandafter\t_syst_helpers_scratch#2}}
+
+\unexpanded\def\prependvalue #1{\expandafter\prepended\expandafter \def\csname#1\endcsname}
+\unexpanded\def\prependgvalue#1{\expandafter\prepended\expandafter\gdef\csname#1\endcsname}
+
+%D \macros
+%D {dowithrange}
+%D
+%D This one is for Mojca Miklavec, who made me aware of the fact that
+%D \type {page-imp.tex} was not the best place to hide it.
+%D
+%D \startbuffer
+%D \def\DoSomething#1{ [item #1] }
+%D
+%D \processranges[1,4:5]\DoSomething \par
+%D \dowithrange {1,4:5}\DoSomething \par
+%D \stopbuffer
+%D
+%D \typebuffer \blank \getbuffer \blank
+
+\def\syst_helpers_with_range#1%
+ {\splitstring#1\at:\to\m_syst_helpers_range_from\and\m_syst_helpers_range_to
+ \ifx\m_syst_helpers_range_to\empty\let\m_syst_helpers_range_to\m_syst_helpers_range_from\fi
+ \dostepwiserecurse\m_syst_helpers_range_from\m_syst_helpers_range_to\plusone{\m_helpers_range_action{##1}}}%
+
+\unexpanded\def\processranges[#1]#2% #1= n:m,p,q:r
+ {\def\m_helpers_range_action{#2}%
+ \processcommacommand[#1]\syst_helpers_with_range}
+
+\unexpanded\def\dowithrange#1#2%
+ {\def\m_helpers_range_action{#2}%
+ \processcommacommand[#1]\syst_helpers_with_range}
+
+% \def\DoSomething#1{ [item #1] }
+% \dowithrange[1,4:5]\DoSomething
+
+%D \macros
+%D {ignoreimplicitspaces}
+%D
+%D \startbuffer
+%D \def\whatever[#1]{\expanded{\definedfont[#1 at 12pt]}\ignorespaces}
+%D {a\whatever[Serif]b a\whatever[Serif] b a\whatever[Serif]\space b}
+%D \def\whatever[#1]{\expanded{\definedfont[#1 at 12pt]}\ignoreimplicitspaces}
+%D {a\whatever[Serif]b a\whatever[Serif] b a\whatever[Serif]\space b}
+%D \stopbuffer
+%D
+%D \typebuffer \getbuffer
+
+\unexpanded\def\ignoreimplicitspaces
+ {\doifelsenextchar\relax\relax\relax}
+
+%D \macros
+%D {processwords}
+%D
+%D Not that sophisticated but sometimes users (like in metafun).
+
+\def\syst_helpers_process_word#1 #2\_e_o_w_
+ {\doifsomething{#1}{\processword{#1} \syst_helpers_process_word#2 \_e_o_w_}}
+
+\def\processwords#1%
+ {\syst_helpers_process_word#1 \_e_o_w_}% no \unskip
+
+\let\processword\relax
+
+%D \macros
+%D {startnointerference}
+%D
+%D \starttyping
+%D \startnointerference
+%D all kind of code
+%D \stopnointerference
+%D \stoptyping
+
+\newbox\b_syst_helpers_no_interference
+
+\unexpanded\def\startnointerference % not even grouped !
+ {\setbox\b_syst_helpers_no_interference\vbox
+ \bgroup}
+
+\unexpanded\def\stopnointerference
+ {\egroup
+ \setbox\b_syst_helpers_no_interference\emptybox}
+
+%D A variant for \type {\executeifdefined}:
+
+\def\expandcheckedcsname#1#2% #2 is often a \xxxparameter so let's expand it once
+ {\normalexpanded{\noexpand\syst_helpers_expand_checked_csname{#1}{#2}}}
+
+\def\syst_helpers_expand_checked_csname#1#2#3%
+ {\csname#1\ifcsname#1#2\endcsname#2\else#3\fi\endcsname}
+
+%D Signal. Some fonts have a char0 rendering so we need to make sure that it
+%D is not set in the font!
+
+\unexpanded\def\signalcharacter{\char\zerocount} % \zwj
+
+%D A few secial variants of commands defined here. Some more will be moved here (e.g.
+%D from table modules.
+
+\def\dodirectdoubleempty#1#2% used in math (lookahead issues)
+ {\ifx#2[%
+ \expandafter\syst_helpers_direct_double_empty_one_yes
+ \else
+ \expandafter\syst_helpers_direct_double_empty_one_nop
+ \fi#1#2}
+
+\def\syst_helpers_direct_double_empty_one_yes#1[#2]#3%
+ {\ifx#3[\else\expandafter\syst_helpers_direct_double_empty_two_nop\fi#1[#2]#3}
+
+\def\syst_helpers_direct_double_empty_one_nop#1{#1[][]}
+\def\syst_helpers_direct_double_empty_two_nop#1[#2]{#1[#2][]}
+
+%D Used in math definitions (in an \type {\edef}):
+
+%D \startbuffer
+%D [\docheckedpair{}]
+%D [\docheckedpair{a}]
+%D [\docheckedpair{a,b}]
+%D [\docheckedpair{a,b,c}]
+%D \stopbuffer
+%D
+%D \typebuffer \startlines \getbuffer \stoplines
+
+\def\docheckedpair#1%
+ {\syst_helpers_checked_pair#1,,\_o_e_p_}
+
+\def\syst_helpers_checked_pair#1,#2,#3\_o_e_p_
+ {#1,#2}
+
+%D Here are some nasty helpers. They can be used to fill often expanded token
+%D lists efficiently (see tabulate for an example).
+
+\def\constantnumber#1%
+ {\ifcase#1\zerocount
+ \or \plusone
+ \or \plustwo
+ \or \plusthree
+ \or \plusfour
+ \or \plusfive
+ \or \plussix
+ \or \plusseven
+ \or \pluseight
+ \or \plusnine
+ \or \plusten
+ \else \number#1\relax\fi}
+
+\def\constantnumberargument#1%
+ {\ifcase#1\zerocount
+ \or \plusone
+ \or \plustwo
+ \or \plusthree
+ \or \plusfour
+ \or \plusfive
+ \or \plussix
+ \or \plusseven
+ \or \pluseight
+ \or \plusnine
+ \or \plusten
+ \else {\number#1}\fi}
+
+\def\constantdimen#1%
+ {\ifdim#1=\zeropoint
+ \zeropoint
+ \else
+ \the#1\relax
+ \fi}
+
+\def\constantdimenargument#1%
+ {\ifdim#1=\zeropoint
+ \zeropoint
+ \else
+ {\the#1}%
+ \fi}
+
+\def\constantemptyargument#1%
+ {\ifx#1\empty
+ \noexpand\empty
+ \else
+ {#1}%
+ \fi}
+
+% %D Maybe some day (moved from cont-new):
+% %D
+% %D \starttyping
+% %D \the\dimexpr(\dimchoice {7pt}{{<10pt}{8pt}{<12pt}{9pt}{<15pt}{10pt}{=11pt}{12pt}})
+% %D \the\dimexpr(\dimchoice{11pt}{{<10pt}{8pt}{<12pt}{9pt}{<15pt}{10pt}{=11pt}{12pt}})
+% %D \the\dimexpr(\dimchoice{14pt}{{<10pt}{8pt}{<12pt}{9pt}{<15pt}{10pt}{=11pt}{12pt}})
+% %D \stoptyping
+%
+% \def\syst_helpers_choice_finish#1\empty{}
+%
+% \def\syst_helpers_choice_dim#1#2#3%
+% {\ifdim#1#2%
+% #3\expandafter\syst_helpers_choice_finish
+% \else
+% \expandafter\syst_helpers_choice_dim
+% \fi{#1}}
+%
+% \def\syst_helpers_choice_num#1#2#3%
+% {\ifnum#1#2%
+% #3\expandafter\syst_helpers_choice_finish
+% \else
+% \expandafter\syst_helpers_choice_num
+% \fi{#1}}
+%
+% \def\dimchoice#1#2{\syst_helpers_choice_dim{#1}#2{=#1}{#1}\empty}
+% \def\numchoice#1#2{\syst_helpers_choice_num{#1}#2{=#1}{#1}\empty}
+
+%D \macros
+%D {getsubstring}
+%D \startbuffer
+%D
+%D \getsubstring{4}{}{Who Wants This}
+%D \getsubstring{4}{9}{Who Wants This}
+%D \getsubstring{9}{-2}{Who Wants This}
+%D \getsubstring{1}{5}{Who Wants This}
+%D \stopbuffer
+%D
+%D \typebuffer
+%D
+%D \startlines
+%D \getbuffer
+%D \stoplines
+
+% expandable:
+
+\def\getsubstring#1#2#3{\clf_getsubstring{#3}{#1}{#2}}
+
+%D Other dimensions than pt (used in mb-mp)
+
+\def\converteddimen#1#2{\clf_converteddimen\dimexpr#1\relax{#2}}
+
+%D Maybe (looks ugly):
+%D
+%D \starttyping
+%D \doifcase {foo}
+%D {bar} {BAR}
+%D {foo} {FOO}
+%D {default} {DEFAULT}
+%D
+%D \doifcase {foo}
+%D {bar} {BAR}
+%D {foo} {\doifcase {bar}
+%D {bar} {BAR}
+%D {foo} {FOO}
+%D {default} {DEFAULT}
+%D }
+%D {default} {DEFAULT}
+%D \stoptyping
+
+% \doifcase {\btxfoundname{author}}
+% {author} {\btxflush{author}}
+% {editor} {\texdefinition{btx:apa:editor-or-editors}}
+% {title} {\texdefinition{btx:apa:title-subtitle-type}}
+% {default} {\btxflush{author}}
+
+% \unexpanded\def\doifcase#1%
+% {\edef\m_case_asked{#1}%
+% \syst_aux_case}
+%
+% \def\syst_aux_case#1%
+% {\edef\m_case_temp{#1}%
+% \ifx\m_case_temp\m_case_asked
+% \expandafter\syst_aux_case_yes
+% \else\ifx\m_case_temp\s!default
+% \doubleexpandafter\firstofoneargument
+% \else
+% \doubleexpandafter\syst_aux_case_nop
+% \fi\fi}
+%
+% \def\syst_aux_skip#1#2%
+% {\edef\m_case_temp{#1}%
+% \ifx\m_case_temp\s!default
+% \expandafter\syst_aux_done
+% \else
+% \expandafter\syst_aux_skip
+% \fi}
+%
+% \def\syst_aux_case_yes#1%
+% {\def\syst_aux_done{#1}%
+% \syst_aux_skip}
+%
+% \def\syst_aux_case_nop#1%
+% {\syst_aux_case}
+
+%D \macros
+%D {ntimes}
+%D
+%D some repetition:
+%D
+%D \startbuffer
+%D \ntimes{*}{20}
+%D \stopbuffer
+%D
+%D \typebuffer \blank gives: \getbuffer \blank
+%D
+%D This is not real fast but quite okay:
+
+%def\ntimes#1#2{\ifnum#2>\zerocount#1\ntimes{#1}{\numexpr#2-\plusone\relax}\fi} % 1.72
+\def\ntimes#1#2{\clf_ntimes{#1}\numexpr#2\relax} % 0.33
+
+\protect \endinput
+
+% \edef\choicetokenyes{+}
+% \edef\choicetokennop{-}
+%
+% \unexpanded\def\startchoice#1%
+% {\pushmacro\currentchoicevalue
+% \edef\currentchoicevalue{#1}%
+% \checkchoicetoken}
+%
+% \unexpanded\def\checkchoicetoken#1%
+% {\edef\choicetoken{#1}%
+% \ifx\choicetoken\choicetokenyes
+% \singleexpandafter\checkchoiceyes
+% \else\ifx\choicetoken\choicetokennop
+% \doubleexpandafter\checkchoicenop
+% \else
+% \doubleexpandafter\choicequit
+% \fi\fi}
+%
+% \def\checkchoiceyes#1%
+% {\edef\choicevalue{#1}%
+% \ifx\currentchoicevalue\choicevalue
+% \expandafter\choiceindeed
+% \else
+% \expandafter\choiceignore
+% \fi}
+%
+% \def\checkchoicenop
+% {\choiceindeed}
+%
+% \def\choiceabort#1\stopchoice
+% {\popmacro\currentchoicevalue}
+%
+% \def\choicequit
+% {\popmacro\currentchoicevalue}
+%
+% \def\choiceindeed#1#2\stopchoice
+% {\popmacro\currentchoicevalue
+% #1}
+%
+% \def\choiceignore#1%
+% {\checkchoicetoken}
+%
+% \let\stopchoice\relax
+%
+% \def\xxx{3}
+% \def\xxx{x}
+%
+% \startchoice {\xxx}
+% + {1} {
+% first
+% } + {2} {
+% second
+% } + {3} {
+% third
+% } - {
+% nothing
+% }
+% \stopchoice
+
+% \def\appendtovaluelist#1#2%
+% {\ifcsname#1\endcsname
+% \expandafter\ifx\csname#1\endcsname\empty
+% \expandafter\def\csname#1\endcsname{#2}%
+% \else
+% \expandafter\def\csname#1\expandafter\expandafter\expandafter\endcsname
+% \expandafter\expandafter\expandafter{\csname#1\endcsname,#2}%
+% \fi
+% \else
+% \expandafter\def\csname#1\endcsname{#2}%
+% \fi}
+%
+% or
+%
+% \def\appendtovaluelist#1%
+% {\ifcsname#1\endcsname
+% \expandafter\ifx\csname#1\endcsname\empty
+% \expandafter\noappendtovaluelist\csname#1\expandafter\expandafter\expandafter\endcsname
+% \else
+% \expandafter\doappendtovaluelist\csname#1\expandafter\expandafter\expandafter\endcsname
+% \fi
+% \else
+% \expandafter\noappendtovaluelist\csname#1\expandafter\endcsname
+% \fi}
+%
+% \def\doappendtovaluelist#1#2{\expandafter\def\expandafter#1\expandafter{#1,#2}}
+% \def\noappendtovaluelist#1#2{\def#1{#2}}
+%
+% \appendtovaluelist{mylist}{aap}
+% \appendtovaluelist{mylist}{noot}
+% \appendtovaluelist{mylist}{mies}
+%
+% \showvalue{mylist}