syst-aux.mkiv /size: 271 Kb    last modification: 2023-12-21 09:44
1%D \module
2%D   [       file=syst-aux, % merge of syst-gen cum suis
3%D        version=1996.03.20,
4%D          title=\CONTEXT\ System Macros,
5%D       subtitle=General,
6%D         author=Hans Hagen,
7%D           date=\currentdate,
8%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
9%C
10%C This module is part of the \CONTEXT\ macro||package and is
11%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
12%C details.
13
14\registerctxluafile{syst-aux}{}
15\registerctxluafile{syst-mac}{}
16
17% A dedicated primitive \ifvoidmacro\cs == \ifx\cs\empty is some 10% faster but
18% probably not that noticeable in practice. An \ifvoidtoks might make sense but we
19% don't test that often for it (and it's more work to implement in the engine).
20
21%D This is a stripped down combination of:
22%D
23%D \startitemize
24%D \item \type {syst-gen.tex}
25%D \item \type {syst-ext.tex}
26%D \item \type {syst-new.tex}
27%D \stopitemize
28%D
29%D We keep them around (for \MKII) so you can find comments, experiences,
30%D intermediate versions and cleaner variants there (and also non-\ETEX\ variants).
31%D
32%D Contrary to the older files, we now assume that this one is used in \CONTEXT\ and
33%D therefore we might also assume that some basic functionality is available.
34%D
35%D The original files contain previous implementations and notes about performance.
36%D This file will be stripped down in due time.
37%D
38%D Some of the macros here were only used in the bibliography module. They have been
39%D be moved to a separate syst module since the bib module is no longer using them.
40%D Some more will go away.
41
42\unprotect
43
44%D \macros
45%D   {unexpanded}
46%D
47%D Because we use this module only in \MKIV, we have removed the old protection
48%D code.
49%D
50%D \starttyping
51%D \protected\def\somecommand{... ... ...}
52%D \stoptyping
53%D
54%D This overloads the \ETEX\ primitive but as we already had an \MKII\ solution we
55%D keep the same name for a similar mechanism.
56
57\let\unexpanded\normalprotected
58
59\def\startlmtxmode#1\stoplmtxmode{}
60\let\stoplmtxmode \relax
61\let\startmkivmode\relax
62\let\stopmkivmode \relax
63
64%D As we don't have namespace definers yet, we use a special one:
65
66\ifdefined\c_syst_helpers_n_of_namespaces
67
68    % lets plug in a better error message
69
70\else
71
72    \newcount\c_syst_helpers_n_of_namespaces \c_syst_helpers_n_of_namespaces\pluseight % 1-8 reserved for catcodes
73
74    \def\v_interfaces_prefix_template_system{\number    \c_syst_helpers_n_of_namespaces>>}
75    %def\v_interfaces_prefix_template_system{\characters\c_syst_helpers_n_of_namespaces>>} % no \characters yet
76
77\fi
78
79\protected\def\installsystemnamespace#1% maybe move this to syst-ini
80  {\ifcsname ??#1\endcsname
81     \writestatus\m!system{duplicate system namespace '#1'}\wait
82   \else
83     \global\advance\c_syst_helpers_n_of_namespaces\plusone
84     \expandafter\edef\csname ??#1\endcsname{\v_interfaces_prefix_template_system}%
85   \fi}
86
87%D \macros
88%D   {normalspace}
89%D
90%D There is already \type{\space} but just to be sure we also provide:
91
92\def\normalspace{ }
93
94%D \macros
95%D   {!!count, !!toks, !!dimen, !!box,
96%D    !!width, !!height, !!depth, !!string, !!done}
97%D
98%D We define some more \COUNTERS\ and \DIMENSIONS. We also define some shortcuts to
99%D the local scatchregisters~0, 2, 4, 6 and~8.
100
101\newcount\!!counta \newtoks\!!toksa \newdimen\!!dimena \newbox\!!boxa
102\newcount\!!countb \newtoks\!!toksb \newdimen\!!dimenb \newbox\!!boxb
103\newcount\!!countc \newtoks\!!toksc \newdimen\!!dimenc \newbox\!!boxc
104\newcount\!!countd \newtoks\!!toksd \newdimen\!!dimend \newbox\!!boxd
105\newcount\!!counte \newtoks\!!tokse \newdimen\!!dimene \newbox\!!boxe
106\newcount\!!countf \newtoks\!!toksf \newdimen\!!dimenf \newbox\!!boxf
107                                    \newdimen\!!dimeng
108                                    \newdimen\!!dimenh
109                                    \newdimen\!!dimeni
110                                    \newdimen\!!dimenj
111                                    \newdimen\!!dimenk
112
113\let\!!stringa\empty \let\!!stringb\empty \let\!!stringc\empty
114\let\!!stringd\empty \let\!!stringe\empty \let\!!stringf\empty
115
116\newdimen\!!widtha \newdimen\!!heighta \newdimen\!!deptha
117\newdimen\!!widthb \newdimen\!!heightb \newdimen\!!depthb
118\newdimen\!!widthc \newdimen\!!heightc \newdimen\!!depthc
119\newdimen\!!widthd \newdimen\!!heightd \newdimen\!!depthd
120
121\newif\if!!donea   \newif\if!!doneb    \newif\if!!donec
122\newif\if!!doned   \newif\if!!donee    \newif\if!!donef
123
124\def\!!zerocount {0} % alongside \zerocount
125\def\!!minusone {-1} % ...
126\def\!!plusone   {1} % ...
127\def\!!plustwo   {2} % ...
128\def\!!plusthree {3} % ...
129\def\!!plusfour  {4} % ...
130\def\!!plusfive  {5} % ...
131\def\!!plussix   {6} % ...
132\def\!!plusseven {7} % ...
133\def\!!pluseight {8} % ...
134\def\!!plusnine  {9} % alongside \plusnine
135
136\setnewconstant   \uprotationangle    0
137\setnewconstant\rightrotationangle   90
138\setnewconstant \downrotationangle  180
139\setnewconstant \leftrotationangle  270
140
141\ifdefined\data \else \let\data \relax \fi % dep checker
142
143%D \macros
144%D   {s!,c!,e!,p!,v!,@@,??}
145%D
146%D To save memory, we use constants (sometimes called variables). Redefining these
147%D constants can have disastrous results.
148
149\def\v!prefix! {v!}
150\def\c!prefix! {c!}
151\def\s!prefix! {s!}
152
153\def\s!next    {next}
154\def\s!default {default}
155\def\s!dummy   {dummy}
156\def\s!unknown {unknown}
157
158\def\s!do      {do}
159\def\s!dodo    {dodo}
160
161\def\s!complex {complex}
162\def\s!start   {start}
163\def\s!simple  {simple}
164\def\s!stop    {stop}
165
166\def\s!empty   {empty}
167
168%D These are not needed any more now that we have wide screens (and bytes come
169%D cheap).
170
171\let\@EA          \singleexpandafter % obsolete
172\let\@EAEAEA      \doubleexpandafter % obsolete
173\let\@EAEAEAEAEAEA\tripleexpandafter % obsolete
174
175%D Sometimes we pass macros as arguments to commands that don't expand them
176%D before interpretation. Such commands can be enclosed with \type {\expanded},
177%D like:
178%D
179%D \starttyping
180%D \expanded{\setupsomething[\alfa]}
181%D \stoptyping
182%D
183%D Such situations occur for instance when \type{\alfa} is a commalist or when data
184%D stored in macros is fed to index of list commands. If needed, one should use
185%D \type{\noexpand} inside the argument. Later on we will meet some more clever
186%D alternatives to this command. Beware, only the simple one has \type {\noexpand}
187%D before its argument.
188
189\let\m_syst_helpers_expanded\empty
190
191\protected\def\expanded#1%
192  {\xdef\m_syst_helpers_expanded{\noexpand#1}\m_syst_helpers_expanded}
193
194\protected\def\startexpanded#1\stopexpanded
195  {\xdef\m_syst_helpers_expanded{#1}\m_syst_helpers_expanded}
196
197\let\stopexpanded\relax
198
199%D Recent \TEX\ engines have a primitive \type {\expanded} and we will use that when
200%D possible. After all, we can make not expandable macros now.
201
202% We cannot use the next variant as first we need to adapt \type {##}'s in callers:
203%
204% \def\expanded#1%
205%   {\normalexpanded{\noexpand#1}}
206%
207% \def\startexpanded#1\stopexpanded
208%   {\normalexpanded{#1}}
209
210%D \macros
211%D   {gobbleoneargument,gobble...arguments}
212%D
213%D The next set of macros just do nothing, except that they get rid of a number of
214%D arguments.
215
216\def\gobbleoneargument   #1{}
217\def\gobbletwoarguments  #1#2{}
218\def\gobblethreearguments#1#2#3{}
219\def\gobblefourarguments #1#2#3#4{}
220\def\gobblefivearguments #1#2#3#4#5{}
221\def\gobblesixarguments  #1#2#3#4#5#6{}
222\def\gobblesevenarguments#1#2#3#4#5#6#7{}
223\def\gobbleeightarguments#1#2#3#4#5#6#7#8{}
224\def\gobbleninearguments #1#2#3#4#5#6#7#8#9{}
225\def\gobbletenarguments  #1{\gobbleninearguments}
226
227\def\gobbleoneoptional   [#1]{}
228\def\gobbletwooptionals  [#1][#2]{}
229\def\gobblethreeoptionals[#1][#2][#3]{}
230\def\gobblefouroptionals [#1][#2][#3][#4]{}
231\def\gobblefiveoptionals [#1][#2][#3][#4][#5]{}
232
233%D Reserved macros for tests:
234
235\let\donothing\empty
236
237\let\m_syst_string_one  \empty
238\let\m_syst_string_two  \empty
239\let\m_syst_string_three\empty
240\let\m_syst_string_four \empty
241
242\let\m_syst_action_yes  \empty
243\let\m_syst_action_nop  \empty
244
245%D \macros
246%D   {doifnextcharelse}
247%D
248%D When we started using \TEX\ in the late eighties, our first experiences with
249%D programming concerned a simple shell around \LATEX. The commands probably use
250%D most at \PRAGMA, are the itemizing ones. One of those few shell commands took
251%D care of an optional argument, that enabled us to specify what kind of item symbol
252%D we wanted. Without understanding anything we were able to locate a \LATEX\ macro
253%D that could be used to inspect the next character.
254%D
255%D It's this macro that the ancester of the next one presented here. It executes one
256%D of two actions, dependant of the next character. Disturbing spaces and line
257%D endings, which are normally interpreted as spaces too, are skipped.
258%D
259%D \starttyping
260%D \doifnextcharelse {karakter} {then ...} {else ...}
261%D \stoptyping
262%D
263%D This macro differs from the original in the use of \type {\localnext} because we
264%D don't want clashes with \type {\next}.
265
266\let\next          \relax
267\let\nextnext      \relax
268\let\nextnextnext  \relax
269\let\nexttoken     \relax
270\let\charactertoken\relax
271
272\let\m_syst_action_yes\relax
273\let\m_syst_action_nop\relax
274
275\def\syst_helpers_inspect_next_character
276  {\ifx\nexttoken\blankspace
277     \expandafter\syst_helpers_reinspect_next_character
278   \else
279     \expandafter\syst_helpers_inspect_next_character_indeed
280   \fi}
281
282\def\syst_helpers_inspect_next_character_indeed
283  {\ifx\nexttoken\charactertoken
284     \expandafter\m_syst_action_yes
285   \else
286     \expandafter\m_syst_action_nop
287   \fi}
288
289\protected\def\doifelsenextchar#1#2#3% #1 should not be {} !
290  {\let\charactertoken=#1% = needed here
291   \def\m_syst_action_yes{#2}%
292   \def\m_syst_action_nop{#3}%
293   \futurelet\nexttoken\syst_helpers_inspect_next_character}
294
295\protected\def\doifelsenextcharcs#1#2#3% #1 should not be {} !
296  {\let\charactertoken=#1% = needed here
297   \let\m_syst_action_yes#2%
298   \let\m_syst_action_nop#3%
299   \futurelet\nexttoken\syst_helpers_inspect_next_character}
300
301\let\doifnextcharelse  \doifelsenextchar
302\let\doifnextcharcselse\doifelsenextcharcs
303
304%D Because we will mostly use this macro for testing if the next character is \type
305%D {[}, we also make a slightly faster variant as it is not uncommon to have tens of
306%D thousands of calls to this test in a run. Of course it also is more convenient to
307%D read a trace then.
308
309%D We could make variants without the \if_next_blank_space_token but the overhead is
310%D only .1 sec on 3.5 for 10^6 tests and often that branch is not entered anyway. The
311%D fast variants with less checking do make a difference however:
312
313% \testfeature{1000000}{\doifnextoptionalelse       \gobbleoneargument\gobbleoneargument[} % 2.902s
314% \testfeature{1000000}{\doifnextoptionalcselse     \gobbleoneargument\gobbleoneargument[} % 2.590s
315% \testfeature{1000000}{\doiffastoptionalcheckelse  \gobbleoneargument\gobbleoneargument[} % 2.387s
316% \testfeature{1000000}{\doiffastoptionalcheckcselse\gobbleoneargument\gobbleoneargument[} % 2.168s
317
318\newif\if_next_blank_space_token
319
320\let\syst_helpers_next_optional_character_token=[
321
322\def\syst_helpers_inspect_next_optional_character
323  {\ifx\nexttoken\blankspace
324     \expandafter\syst_helpers_reinspect_next_optional_character
325   \else
326     \expandafter\syst_helpers_inspect_next_optional_character_indeed
327   \fi}
328
329\def\syst_helpers_inspect_next_optional_character_indeed
330  {\ifx\nexttoken\syst_helpers_next_optional_character_token
331     \expandafter\m_syst_action_yes
332   \else
333     \expandafter\m_syst_action_nop
334   \fi}
335
336\protected\def\doifelsenextoptional#1#2%
337  {\def\m_syst_action_yes{#1}%
338   \def\m_syst_action_nop{#2}%
339   \let\if_next_blank_space_token\iffalse
340   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
341
342\protected\def\doifelsenextoptionalcs#1#2% \cs \cs (upto 10% faster)
343  {\let\m_syst_action_yes#1%
344   \let\m_syst_action_nop#2%
345   \let\if_next_blank_space_token\iffalse
346   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
347
348\let\doifnextoptionalelse  \doifelsenextoptional
349\let\doifnextoptionalcselse\doifelsenextoptionalcs
350
351% fails on assignments
352%
353% \protected\def\doifelsenextoptional      {\afterassignment\doifelsenextoptional_n  \def\m_syst_action_yes}
354%           \def\doifelsenextoptional_n    {\afterassignment\doifelsenextoptional_n_n\def\m_syst_action_nop}
355%           \def\doifelsenextoptional_n_n  {\let\if_next_blank_space_token\iffalse
356%                                            \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
357%
358% \protected\def\doifelsenextoptionalcs    {\afterassignment\doifelsenextoptionalcs_n  \let\m_syst_action_yes}
359%           \def\doifelsenextoptionalcs_n  {\afterassignment\doifelsenextoptionalcs_n_n\let\m_syst_action_nop}
360%           \def\doifelsenextoptionalcs_n_n{\let\if_next_blank_space_token\iffalse
361%                                            \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
362
363\let\syst_helpers_next_bgroup_character_token\bgroup
364
365\def\syst_helpers_inspect_next_bgroup_character
366  {\ifx\nexttoken\blankspace
367     \expandafter\syst_helpers_reinspect_next_bgroup_character
368   \else
369     \expandafter\syst_helpers_inspect_next_bgroup_character_indeed
370   \fi}
371
372\def\syst_helpers_inspect_next_bgroup_character_indeed
373  {\ifx\nexttoken\syst_helpers_next_bgroup_character_token
374     \expandafter\m_syst_action_yes
375   \else
376     \expandafter\m_syst_action_nop
377   \fi}
378
379\protected\def\doifelsenextbgroup#1#2%
380  {\def\m_syst_action_yes{#1}%
381   \def\m_syst_action_nop{#2}%
382   \let\if_next_blank_space_token\iffalse
383   \futurelet\nexttoken\syst_helpers_inspect_next_bgroup_character}
384
385\protected\def\doifelsenextbgroupcs#1#2%
386  {\let\m_syst_action_yes#1%
387   \let\m_syst_action_nop#2%
388   \let\if_next_blank_space_token\iffalse
389   \futurelet\nexttoken\syst_helpers_inspect_next_bgroup_character}
390
391\let\doifnextbgroupelse  \doifelsenextbgroup
392\let\doifnextbgroupcselse\doifelsenextbgroupcs
393
394\let\syst_helpers_next_parenthesis_character_token(
395
396\def\syst_helpers_inspect_next_parenthesis_character
397  {\ifx\nexttoken\blankspace
398     \expandafter\syst_helpers_reinspect_next_parenthesis_character
399   \else
400     \expandafter\syst_helpers_inspect_next_parenthesis_character_indeed
401   \fi}
402
403\def\syst_helpers_inspect_next_parenthesis_character_indeed
404  {\ifx\nexttoken\syst_helpers_next_parenthesis_character_token
405     \expandafter\m_syst_action_yes
406   \else
407     \expandafter\m_syst_action_nop
408   \fi}
409
410\protected\def\doifelsenextparenthesis#1#2%
411  {\def\m_syst_action_yes{#1}%
412   \def\m_syst_action_nop{#2}%
413   \let\if_next_blank_space_token\iffalse
414   \futurelet\nexttoken\syst_helpers_inspect_next_parenthesis_character}
415
416\let\doifnextparenthesiselse\doifelsenextparenthesis
417
418%D The next one is handy in predictable situations:
419
420\def\syst_helpers_do_if_fast_optional_check_else
421  {\ifx\nexttoken\syst_helpers_next_optional_character_token
422     \expandafter\m_syst_action_yes
423   \else
424     \expandafter\m_syst_action_nop
425   \fi}
426
427\protected\def\doifelsefastoptionalcheck#1#2%
428  {\def\m_syst_action_yes{#1}%
429   \def\m_syst_action_nop{#2}%
430   \futurelet\nexttoken\syst_helpers_do_if_fast_optional_check_else}
431
432\protected\def\doifelsefastoptionalcheckcs#1#2% \cs \cs
433  {\let\m_syst_action_yes#1%
434   \let\m_syst_action_nop#2%
435   \futurelet\nexttoken\syst_helpers_do_if_fast_optional_check_else}
436
437\let\doiffastoptionalcheckelse  \doifelsefastoptionalcheck
438\let\doiffastoptionalcheckcselse\doifelsefastoptionalcheckcs
439
440%D Here's one for skipping spaces and pars, handy for:
441%D
442%D \starttyping
443%D \hbox
444%D
445%D   {a few lines later}
446%D \stoptyping
447
448% \protected\def\assumelongusagecs#1%
449%   {\let\m_syst_action#1%
450%    \futurelet\nexttoken\syst_helpers_ignore_par_character}
451%
452% \def\syst_helpers_ignore_par_character
453%   {\ifx\nexttoken\blankspace
454%      \expandafter\syst_helpers_ignore_par_character_blankspace
455%    \else
456%      \expandafter\syst_helpers_ignore_par_character_followup
457%    \fi}
458%
459% \def\syst_helpers_ignore_par_character_followup
460%   {\ifx\nexttoken\par
461%      \expandafter\syst_helpers_ignore_par_partoken
462%    \else
463%      \expandafter\m_syst_action
464%    \fi}
465%
466% \def\syst_helpers_ignore_par_partoken
467%   {\afterassignment\m_syst_action\let\nexttoken}
468
469\protected\def\assumelongusagecs#1%
470  {\let\m_syst_action#1%
471   \futurelet\nexttoken\syst_helpers_ignore_spacing}
472
473\def\syst_helpers_ignore_spacing
474  {\ifx\nexttoken\blankspace
475     \singleexpandafter\syst_helpers_ignore_spacing_blankspace
476   \else\ifx\nexttoken\par
477     \doubleexpandafter\syst_helpers_ignore_spacing_partoken
478   \else
479     \doubleexpandafter\m_syst_action
480   \fi\fi}
481
482\def\syst_helpers_ignore_spacing_partoken\par
483  {\futurelet\nexttoken\syst_helpers_ignore_spacing}
484
485%D These macros use some auxiliary macros. Although we were able to program quite
486%D complicated things, I only understood these after rereading the \TEX book. The
487%D trick is in using a command with a one character name. Such commands differ from
488%D the longer ones in the fact that trailing spaces are {\em not} skipped. This
489%D enables us to indirectly define a long named macro that gobbles a space. In the
490%D first line we define \type {\blankspace}. Next we make \type {\:} equivalent to
491%D \type {\reinspect...}. This one||character command is expanded before the next
492%D \type {\def} comes into action. This way the space after \type {\:} becomes a
493%D delimiter of the longer named \type {\reinspectnextcharacter}.
494
495% \ifcase\contextlmtxmode
496
497    \let\next\:
498
499    \def\:{\let\blankspace= }  \:
500
501    \def\:{\syst_helpers_reinspect_next_character}
502    \expandafter\def\: {\let\if_next_blank_space_token\iftrue\futurelet\nexttoken\syst_helpers_inspect_next_character}
503
504    \def\:{\syst_helpers_reinspect_next_optional_character}
505    \expandafter\def\: {\let\if_next_blank_space_token\iftrue\futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
506
507    \def\:{\syst_helpers_reinspect_next_bgroup_character}
508    \expandafter\def\: {\let\if_next_blank_space_token\iftrue\futurelet\nexttoken\syst_helpers_inspect_next_bgroup_character}
509
510    \def\:{\syst_helpers_reinspect_next_parenthesis_character}
511    \expandafter\def\: {\let\if_next_blank_space_token\iftrue\futurelet\nexttoken\syst_helpers_inspect_next_parenthesis_character}
512
513    \def\:{\syst_helpers_ignore_spacing_blankspace}
514    \expandafter\def\: {\futurelet\nexttoken\syst_helpers_ignore_spacing}
515
516    \let\:\next
517
518% \else
519%
520%     % nothing here
521%
522% \fi
523
524%D This is much nicer and works too:
525
526% \def\firstofoneargument#1{#1}
527%
528% \expandafter\let\firstofoneargument{\blankspace= }
529%
530% \expandafter\def\firstofoneargument{\syst_helpers_reinspect_next_character
531%   } {\let\if_next_blank_space_token\iftrue
532%      \futurelet\nexttoken\syst_helpers_inspect_next_character}
533%
534% \expandafter\def\firstofoneargument{\syst_helpers_reinspect_next_optional_character
535%   } {\let\if_next_blank_space_token\iftrue
536%      \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
537%
538% \expandafter\def\firstofoneargument{\syst_helpers_reinspect_next_bgroup_character
539%   } {\let\if_next_blank_space_token\iftrue
540%      \futurelet\nexttoken\syst_helpers_inspect_next_bgroup_character}
541%
542% \expandafter\def\firstofoneargument{\syst_helpers_reinspect_next_parenthesis_character
543%   } {\let\if_next_blank_space_token\iftrue
544%      \futurelet\nexttoken\syst_helpers_inspect_next_parenthesis_character}
545%
546% \expandafter\def\firstofoneargument{\syst_helpers_ignore_spacing_blankspace
547%   } {\futurelet\nexttoken\syst_helpers_ignore_spacing}
548
549%D \macros
550%D   {setvalue,setgvalue,setevalue,setxvalue,
551%D    letvalue,letgvalue,getvalue,resetvalue,
552%D    undefinevalue,ignorevalue}
553%D
554%D \TEX's primitive \type {\csname} can be used to construct all kind of commands
555%D that cannot be defined with \type {\def} and \type {\let}. Every macro programmer
556%D sooner or later wants macros like these.
557%D
558%D \starttyping
559%D \setvalue   {name}{...} = \def\name{...}
560%D \setgvalue  {name}{...} = \gdef\name{...}
561%D \setevalue  {name}{...} = \edef\name{...}
562%D \setxvalue  {name}{...} = \xdef\name{...}
563%D \letvalue   {name}=\... = \let\name=\...
564%D \letgvalue  {name}=\... = \glet\name=\...
565%D \getvalue   {name}      = \name
566%D \resetvalue {name}      = \def\name{}
567%D \stoptyping
568%D
569%D As we will see, \CONTEXT\ uses these commands many times, which is mainly due to
570%D its object oriented and parameter driven character.
571
572\def\setvalue     #1{\expandafter\def \csname#1\endcsname}
573\def\setgvalue    #1{\expandafter\gdef\csname#1\endcsname}
574\def\setevalue    #1{\expandafter\edef\csname#1\endcsname}
575\def\setxvalue    #1{\expandafter\xdef\csname#1\endcsname}
576\def\getvalue     #1{\begincsname#1\endcsname}
577\def\letvalue     #1{\expandafter\let \csname#1\endcsname}
578\def\letgvalue    #1{\expandafter\glet\csname#1\endcsname}
579\def\resetvalue   #1{\expandafter\let \csname#1\endcsname\empty}
580\def\undefinevalue#1{\expandafter\let \csname#1\endcsname\undefined}
581\def\ignorevalue#1#2{\expandafter\let \csname#1\endcsname\empty}
582
583\def\setuvalue    #1{\protected\expandafter \def\csname#1\endcsname}
584\def\setuevalue   #1{\protected\expandafter\edef\csname#1\endcsname}
585\def\setugvalue   #1{\protected\expandafter\gdef\csname#1\endcsname}
586\def\setuxvalue   #1{\protected\expandafter\xdef\csname#1\endcsname}
587
588\protected\def\getuvalue#1{\begincsname#1\endcsname}
589
590%D \macros
591%D   {globallet,glet}
592%D
593%D In \CONTEXT\ of May 2000 using \type {\globallet} instead of the two tokens will
594%D save us some $300\times4=1200$ bytes of format file on a 32~bit system. Not that
595%D it matters much today. This shortcut is already defined:
596
597\let\globallet\glet
598
599%D \macros
600%D   {doifundefined,doifdefined,
601%D    doifundefinedelse,doifdefinedelse,
602%D    doifalldefinedelse}
603%D
604%D The standard way of testing if a macro is defined is comparing its meaning with
605%D another undefined one, usually \type{\undefined}. To garantee correct working of
606%D the next set of macros, \type{\undefined} may never be defined!
607%D
608%D \starttyping
609%D \doifundefined      {string}    {...}
610%D \doifdefined        {string}    {...}
611%D \doifundefinedelse  {string}    {then ...} {else ...}
612%D \doifdefinedelse    {string}    {then ...} {else ...}
613%D \doifalldefinedelse {commalist} {then ...} {else ...}
614%D \stoptyping
615%D
616%D Every macroname that \TEX\ builds gets an entry in the hash table, which is of
617%D limited size. It is expected that \ETEX\ will offer a less memory||consuming
618%D alternative.
619
620%D Although it will probably never be a big problem, it is good to be aware of the
621%D difference between testing on a macro name to be build by using \type{\csname} and
622%D \type{\endcsname} and testing the \type{\name} directly.
623%D
624%D \starttyping
625%D \expandafter\ifx\csname NameA\endcsname\relax ... \else ... \fi
626%D
627%D \ifundefined\NameB ... \else ... \fi
628%D \stoptyping
629
630% \suppressifcsnameerror\plusone % already set
631
632\def\doifelseundefined#1%
633  {\ifcsname#1\endcsname
634     \expandafter\secondoftwoarguments\else\expandafter\firstoftwoarguments
635   \fi}
636
637\def\doifelsedefined#1%
638  {\ifcsname#1\endcsname
639     \expandafter\firstoftwoarguments\else\expandafter\secondoftwoarguments
640   \fi}
641
642\def\doifundefined#1%
643  {\ifcsname#1\endcsname
644     \expandafter\gobbleoneargument\else\expandafter\firstofoneargument
645   \fi}
646
647\def\doifdefined#1%
648  {\ifcsname#1\endcsname
649     \expandafter\firstofoneargument\else\expandafter\gobbleoneargument
650   \fi}
651
652\let\doifundefinedelse\doifelseundefined
653\let\doifdefinedelse  \doifelsedefined
654
655%D \macros
656%D   {letbeundefined}
657%D
658%D Testing for being undefined comes down to testing on \type {\relax} when we use
659%D \type {\csname}, but when using \type {\ifx}, we test on being \type
660%D {\undefined}! In \ETEX\ we have \type {\ifcsname} and that way of testing on
661%D existance is not the same as the one described here. Therefore we introduce:
662
663\protected\def\letbeundefined#1% potential stack buildup when used \global
664  {\expandafter\let\csname#1\endcsname\undefined} % or use \undefinevalue to match \setvalue
665
666\protected\def\localundefine#1% conditional
667  {\ifcsname#1\endcsname\expandafter\let\csname#1\endcsname\undefined\fi}
668
669\protected\def\globalundefine#1% conditional
670  {\ifcsname#1\endcsname\expandafter\glet\csname#1\endcsname\undefined\fi}
671
672%D Beware, being \type {\undefined} in \ETEX\ means that the macro {\em is} defined!
673%D
674%D When we were developing the scientific units module, we encountered different
675%D behavior in text and math mode, which was due to this grouping subtilities. We
676%D therefore decided to use \type{\begingroup} instead of \type{\bgroup}.
677
678\protected\def\doifelsealldefined#1%
679  {\begingroup
680   \donetrue % we could use a reserved one and avoid the group
681   \processcommalist[#1]\syst_helpers_do_if_all_defined_else
682   \ifdone
683     \endgroup\expandafter\firstoftwoarguments
684   \else
685     \endgroup\expandafter\secondoftwoarguments
686   \fi}
687
688\let\doifalldefinedelse\doifelsealldefined
689
690\def\syst_helpers_do_if_all_defined_else#1%
691  {\ifcsname#1\endcsname\else
692     \donefalse
693     \expandafter\quitcommalist % added
694   \fi}
695
696%D \macros
697%D   {doif,doifelse,doifnot}
698%D
699%D Programming in \TEX\ differs from programming in procedural languages like
700%D \MODULA. This means that one --- well, let me speek for myself --- tries to do
701%D the things in the well known way. Therefore the next set of \type{\ifthenelse}
702%D commands were between the first ones we needed. A few years later, the opposite
703%D became true: when programming in \MODULA, I sometimes miss handy things like
704%D grouping, runtime redefinition, expansion etc. While \MODULA\ taught me to
705%D structure, \TEX\ taught me to think recursive.
706%D
707%D \starttyping
708%D \doif     {string1} {string2} {...}
709%D \doifnot  {string1} {string2} {...}
710%D \doifelse {string1} {string2} {then ...}{else ...}
711%D \stoptyping
712
713\protected\def\doif#1#2%
714  {\edef\m_syst_string_one{#1}%
715   \edef\m_syst_string_two{#2}%
716   \ifx\m_syst_string_one\m_syst_string_two
717     \expandafter\firstofoneargument
718   \else
719     \expandafter\gobbleoneargument
720   \fi}
721
722\protected\def\doifnot#1#2%
723  {\edef\m_syst_string_one{#1}%
724   \edef\m_syst_string_two{#2}%
725   \ifx\m_syst_string_one\m_syst_string_two
726     \expandafter\gobbleoneargument
727   \else
728     \expandafter\firstofoneargument
729   \fi}
730
731\protected\def\doifelse#1#2%
732  {\edef\m_syst_string_one{#1}%
733   \edef\m_syst_string_two{#2}%
734   \ifx\m_syst_string_one\m_syst_string_two
735     \expandafter\firstoftwoarguments
736   \else
737     \expandafter\secondoftwoarguments
738   \fi}
739
740%D \macros
741%D   {doifempty,doifemptyelse,doifnotempty}
742%D
743%D We complete our set of conditionals with:
744%D
745%D \starttyping
746%D \doifempty     {string} {...}
747%D \doifnotempty  {string} {...}
748%D \doifemptyelse {string} {then ...} {else ...}
749%D \stoptyping
750%D
751%D This time, the string is not expanded.
752
753\protected\def\doifelseempty#1%
754  {\def\m_syst_string_one{#1}%
755   \ifx\m_syst_string_one\empty
756     \expandafter\firstoftwoarguments
757   \else
758     \expandafter\secondoftwoarguments
759   \fi}
760
761\let\doifemptyelse\doifelseempty
762
763\protected\def\doifempty#1%
764  {\def\m_syst_string_one{#1}%
765   \ifx\m_syst_string_one\empty
766     \expandafter\firstofoneargument
767   \else
768     \expandafter\gobbleoneargument
769   \fi}
770
771\protected\def\doifnotempty#1%
772  {\def\m_syst_string_one{#1}%
773   \ifx\m_syst_string_one\empty
774     \expandafter\gobbleoneargument
775   \else
776     \expandafter\firstofoneargument
777   \fi}
778
779%D \macros
780%D   {doifinset,doifnotinset,doifinsetelse}
781%D
782%D We can check if a string is present in a comma separated set of strings.
783%D Depending on the result, some action is taken.
784%D
785%D \starttyping
786%D \doifinset     {string} {string,...} {...}
787%D \doifnotinset  {string} {string,...} {...}
788%D \doifinsetelse {string} {string,...} {then ...} {else ...}
789%D \stoptyping
790
791% !0nop=\doifinsetelse{ccc}{,}{yes}{nop}
792% !0nop=\doifinsetelse{ccc}{,,}{yes}{nop}
793% !0nop=\doifinsetelse{ccc}{,,,}{yes}{nop}
794
795% !1nop=\doifinsetelse{}{}{yes}{nop}
796% !2yes=\doifinsetelse{aaa}{bbb,ccc,ddd,aaa,eee}{yes}{nop}
797% !3nop=\doifinsetelse{aaa}{bbb}{yes}{nop}
798% !4yes=\doifinsetelse{aaa}{aaa}{yes}{nop}
799% !5nop=\doifinsetelse{aaaa}{bbb,ccc,ddd,aaa,eee}{yes}{nop}
800% !6nop=\doifinsetelse{}{}{yes}{nop}
801% !7nop=\doifinsetelse{}{aaa}{yes}{nop}
802% !8nop=\doifinsetelse{aaa}{}{yes}{nop}
803
804% !1=\doifinset{}{}{yes}
805% !2yes=\doifinset{aaa}{bbb,ccc,ddd,aaa,eee}{yes}
806% !3=\doifinset{aaa}{bbb}{yes}
807% !4yes=\doifinset{aaa}{aaa}{yes}
808% !5=\doifinset{}{}{yes}
809% !6=\doifinset{aaa}{}{yes}
810
811% !1yes=\doifnotinset{}{}{yes}
812% !2=\doifnotinset{aaa}{bbb,ccc,ddd,aaa,eee}{yes}
813% !3yes=\doifnotinset{aaa}{bbb}{yes}
814% !4=\doifnotinset{aaa}{aaa}{yes}
815% !5yes=\doifnotinset{}{}{yes}
816% !6yes=\doifnotinset{aaa}{}{yes}
817
818% \def\v_syst_helpers_right_optional_bracket{]}
819%
820% \def\syst_helpers_do_quit_if_item_in_set_else#1],\relax{\firstoftwoarguments}
821% \def\syst_helpers_do_quit_if_item_in_set     #1],\relax{\firstofoneargument}
822% \def\syst_helpers_do_quit_if_item_not_in_set #1],\relax{\gobbleoneargument}
823%
824% \def\syst_helpers_re_do_if_in_set_else{\expandafter\syst_helpers_do_check_if_item_in_set_else\m_syst_string_two,],\relax}
825% \def\syst_helpers_re_do_if_in_set     {\expandafter\syst_helpers_do_check_if_item_in_set     \m_syst_string_two,],\relax}
826% \def\syst_helpers_re_do_if_not_in_set {\expandafter\syst_helpers_do_check_if_item_not_in_set \m_syst_string_two,],\relax}
827%
828% \protected\def\doifelseinset#1% make this two step too
829%   {\edef\m_syst_string_one{#1}%
830%    \ifx\m_syst_string_one\empty
831%      \expandafter\thirdofthreearguments
832%    \else
833%      \expandafter\syst_helpers_do_if_in_set_else
834%    \fi}
835%
836% \let\doifinsetelse\doifelseinset
837%
838% \def\syst_helpers_do_if_in_set_else#1%
839%   {\edef\m_syst_string_two{#1}%
840%    \ifx\m_syst_string_two\empty
841%      \expandafter\secondoftwoarguments
842%    \else
843%      \expandafter\syst_helpers_re_do_if_in_set_else
844%    \fi}
845%
846% \protected\def\doifinset#1%
847%   {\edef\m_syst_string_one{#1}%
848%    \ifx\m_syst_string_one\empty
849%      \expandafter\gobbletwoarguments
850%    \else
851%      \expandafter\syst_helpers_do_if_in_set
852%    \fi}
853%
854% \def\syst_helpers_do_if_in_set#1%
855%   {\edef\m_syst_string_two{#1}%
856%    \ifx\m_syst_string_two\empty
857%      \expandafter\gobbleoneargument
858%    \else
859%      \expandafter\syst_helpers_re_do_if_in_set
860%    \fi}
861%
862% \protected\def\doifnotinset#1%
863%   {\edef\m_syst_string_one{#1}%
864%    \ifx\m_syst_string_one\empty
865%      \expandafter\secondoftwoarguments
866%    \else
867%      \expandafter\syst_helpers_do_if_not_in_set
868%    \fi}
869%
870% \def\syst_helpers_do_if_not_in_set#1%
871%   {\edef\m_syst_string_two{#1}%
872%    \ifx\m_syst_string_two\empty
873%      \expandafter\firstofoneargument
874%    \else
875%      \expandafter\syst_helpers_re_do_if_not_in_set % ...]{true}
876%    \fi}
877%
878% \def\syst_helpers_do_check_if_item_in_set_else#1,#2% #2 eats up preceding space
879%   {\edef\m_syst_string_two{#1}%
880%    \ifx\m_syst_string_two\empty
881%      \expandafter\syst_helpers_do_check_if_item_in_set_else
882%    \else
883%      \expandafter\syst_helpers_do_do_check_if_item_in_set_else
884%    \fi#2}
885%
886% \def\syst_helpers_do_do_check_if_item_in_set_else
887%   {\ifx\m_syst_string_two\v_syst_helpers_right_optional_bracket
888%      \expandafter\thirdofthreearguments
889%    \else
890%      \expandafter\syst_helpers_do_do_do_check_if_item_in_set_else
891%    \fi}
892%
893% \def\syst_helpers_do_do_do_check_if_item_in_set_else
894%   {\ifx\m_syst_string_one\m_syst_string_two
895%      \expandafter\syst_helpers_do_quit_if_item_in_set_else
896%    \else
897%      \expandafter\syst_helpers_do_check_if_item_in_set_else
898%    \fi}
899%
900% \def\syst_helpers_do_check_if_item_in_set#1,#2% #2 eats up preceding space
901%   {\edef\m_syst_string_two{#1}%
902%    \ifx\m_syst_string_two\empty
903%      \expandafter\syst_helpers_do_check_if_item_in_set
904%    \else
905%      \expandafter\syst_helpers_do_do_check_if_item_in_set
906%    \fi#2}
907%
908% \def\syst_helpers_do_do_check_if_item_in_set
909%   {\ifx\m_syst_string_two\v_syst_helpers_right_optional_bracket
910%      \expandafter\gobbletwoarguments
911%    \else
912%      \expandafter\syst_helpers_do_do_do_check_if_item_in_set
913%    \fi}
914%
915% \def\syst_helpers_do_do_do_check_if_item_in_set
916%   {\ifx\m_syst_string_one\m_syst_string_two
917%      \expandafter\syst_helpers_do_quit_if_item_in_set
918%    \else
919%      \expandafter\syst_helpers_do_check_if_item_in_set
920%    \fi}
921%
922% \def\syst_helpers_do_check_if_item_not_in_set#1,#2% #2 eats up preceding space
923%   {\edef\m_syst_string_two{#1}%
924%    \ifx\m_syst_string_two\empty
925%      \expandafter\syst_helpers_do_check_if_item_not_in_set
926%    \else
927%      \expandafter\syst_helpers_do_do_check_if_item_not_in_set
928%    \fi#2}
929%
930% \def\syst_helpers_do_do_check_if_item_not_in_set
931%   {\ifx\m_syst_string_two\v_syst_helpers_right_optional_bracket
932%      \expandafter\secondoftwoarguments
933%    \else
934%      \expandafter\syst_helpers_do_do_do_check_if_item_not_in_set
935%    \fi}
936%
937% \def\syst_helpers_do_do_do_check_if_item_not_in_set
938%   {\ifx\m_syst_string_one\m_syst_string_two
939%      \expandafter\syst_helpers_do_quit_if_item_not_in_set
940%    \else
941%      \expandafter\syst_helpers_do_check_if_item_not_in_set
942%    \fi}
943
944\protected\def\doifelseinset#1#2{\clf_doifelseinset{#1}{#2}}
945\protected\def\doifinset    #1#2{\clf_doifinset    {#1}{#2}}
946\protected\def\doifnotinset #1#2{\clf_doifnotinset {#1}{#2}}
947         % \let\firstinset        \clf_firstinset
948
949\let\doifinsetelse\doifelseinset
950
951%D \macros
952%D   {doifcommon,doifnotcommon,doifcommonelse}
953%D
954%D Probably the most time consuming tests are those that test for overlap in sets
955%D of strings.
956%D
957%D \starttyping
958%D \doifcommon     {string,...} {string,...} {...}
959%D \doifnotcommon  {string,...} {string,...} {...}
960%D \doifcommonelse {string,...} {string,...} {then ...} {else ...}
961%D \stoptyping
962
963% !1yes=\doifcommonelse{aaa,bbb,ccc}{aaa,bbb,ccc}{yes}{nop}
964% !2nop=\doifcommonelse{aaa,bbb,ccc}{ddd,eee,fff}{yes}{nop}
965% !3nop=\doifcommonelse{aaa}{ddd,eee,fff}{yes}{nop}
966% !4yes=\doifcommonelse{aaa}{aaa}{yes}{nop}
967% !5nop=\doifcommonelse{bbb}{aaa}{yes}{nop}
968% !6nop=\doifcommonelse{}{aaa,bbb,ccc}{yes}{nop}
969% !7nop=\doifcommonelse{aaa,bbb,ccc}{}{yes}{nop}
970% !8nop=\doifcommonelse{}{}{yes}{nop}
971
972% !9nop=\doifcommonelse{,,}{,,}{yes}{nop}
973% !9yes=\doifcommonelse{,a,}{,a,}{yes}{nop}
974% !9yes=\doifcommonelse{,,a,}{,a,}{yes}{nop}
975% !9yes=\doifcommonelse{,a,}{,,a,}{yes}{nop}
976% !9yes=\doifcommonelse{,a,}{,,,a,}{yes}{nop}
977% !9yes=\doifcommonelse{,,a,}{,,,a,}{yes}{nop}
978
979% \let\m_syst_common_a\empty
980% \let\m_syst_common_b\empty
981% \let\m_syst_common_c\empty
982%
983% \def\syst_helpers_do_quit_if_common_else#1],\relax#2],\relax{\firstoftwoarguments}
984%
985% \def\syst_helpers_do_check_if_common_else_one#1,#2% hm, why #2 here and passed at end
986%   {\edef\m_syst_common_c{#1}%
987%    \ifx\m_syst_common_c\v_syst_helpers_right_optional_bracket
988%      \expandafter\thirdofthreearguments
989%    \else
990%      \expandafter\syst_helpers_do_common_check
991%    \fi#2}
992%
993% \def\syst_helpers_do_check_if_common_else_two#1,#2% we can do an empty #1 check too
994%   {\edef\commalistelement{#1}%
995%    \ifx\commalistelement\v_syst_helpers_right_optional_bracket
996%      \expandafter\syst_helpers_re_do_check_if_common_else_one
997%    \else
998%      \expandafter\syst_helpers_do_do_check_if_common_else_two
999%    \fi#2}
1000%
1001% \def\syst_helpers_do_do_check_if_common_else_two
1002%   {\ifx\commalistelement\empty
1003%      \expandafter\syst_helpers_do_check_if_common_else_two
1004%    \else
1005%      \expandafter\syst_helpers_do_do_do_check_if_common_else_two
1006%    \fi}
1007%
1008% \def\syst_helpers_do_do_do_check_if_common_else_two
1009%   {\ifx\m_syst_common_c\commalistelement
1010%      \expandafter\syst_helpers_do_quit_if_common_else
1011%    \else
1012%      \expandafter\syst_helpers_do_check_if_common_else_two
1013%    \fi}
1014%
1015% \def\syst_helpers_re_do_check_if_common_else_one#1{\syst_helpers_do_check_if_common_else_one}
1016%
1017% \def\syst_helpers_do_common_check
1018%   {\expandafter\syst_helpers_do_check_if_common_else_two\m_syst_common_b,],\relax}%
1019%
1020% \def\syst_helpers_do_do_do_if_common_else
1021%   {\expandafter\syst_helpers_do_check_if_common_else_one\m_syst_common_a,],\relax}
1022%
1023% \def\syst_helpers_do_do_if_common_else#1#2#3#4%
1024%   {\edef\m_syst_common_a{#3}%
1025%    \edef\m_syst_common_b{#4}%
1026%    \ifx\m_syst_common_a\empty
1027%      \expandafter\secondoftwoarguments
1028%    \else\ifx\m_syst_common_b\empty
1029%      \expandafter\expandafter\expandafter\secondoftwoarguments
1030%    \else
1031%      \expandafter\expandafter\expandafter\syst_helpers_do_do_do_if_common_else
1032%    \fi\fi
1033%    #1#2}
1034
1035% \protected\def\doifelsecommon{\syst_helpers_do_do_if_common_else\firstoftwoarguments\secondoftwoarguments}
1036% \protected\def\doifcommon    {\syst_helpers_do_do_if_common_else\firstofoneargument \gobbleoneargument   }
1037% \protected\def\doifnotcommon {\syst_helpers_do_do_if_common_else\gobbleoneargument  \firstofoneargument  }
1038%
1039% \let\doifcommonelse\doifelsecommon
1040
1041\protected\def\doifelsecommon#1#2{\clf_doifelsecommon{#1}{#2}}
1042\protected\def\doifcommon    #1#2{\clf_doifcommon    {#1}{#2}}
1043\protected\def\doifnotcommon #1#2{\clf_doifnotcommon {#1}{#2}}
1044
1045\let\doifcommonelse\doifelsecommon
1046
1047%D \macros
1048%D   {processcommalist,processcommacommand,quitcommalist,
1049%D    processcommalistwithparameters}
1050%D
1051%D We've already seen some macros that take care of comma separated lists. Such
1052%D list can be processed with
1053%D
1054%D \starttyping
1055%D \processcommalist[string,string,...]\commando
1056%D \stoptyping
1057%D
1058%D The user supplied command \type{\commando} receives one argument: the string.
1059%D This command permits nesting and spaces after commas are skipped. Empty sets
1060%D are no problem.
1061%D
1062%D \startbuffer
1063%D \def\dosomething#1{(#1)}
1064%D
1065%D 1: \processcommalist [\hbox{$a,b,c,d,e,f$}] \dosomething \par
1066%D 2: \processcommalist [{a,b,c,d,e,f}]        \dosomething \par
1067%D 3: \processcommalist [{a,b,c},d,e,f]        \dosomething \par
1068%D 4: \processcommalist [a,b,{c,d,e},f]        \dosomething \par
1069%D 5: \processcommalist [a{b,c},d,e,f]         \dosomething \par
1070%D 6: \processcommalist [{a,b}c,d,e,f]         \dosomething \par
1071%D 7: \processcommalist []                     \dosomething \par
1072%D 8: \processcommalist [{[}]                  \dosomething \par
1073%D \stopbuffer
1074%D
1075%D \typebuffer
1076%D
1077%D Before we show the result, we present the macro's:
1078
1079\newcount\commalevel
1080
1081\installsystemnamespace{nextcommalevel}
1082
1083\def\syst_helpers_do_do_do_process_comma_item
1084  {\csname\??nextcommalevel\the\commalevel\endcsname}
1085
1086% \ifcase\contextlmtxmode
1087
1088    \def\syst_helpers_do_process_comma_item
1089      {\futurelet\nexttoken\syst_helpers_do_do_process_comma_item}
1090
1091    \def\syst_helpers_do_do_process_comma_item
1092      {\ifx\nexttoken\blankspace
1093         \expandafter\syst_helpers_re_do_process_comma_item
1094       \else
1095         \expandafter\syst_helpers_do_do_process_comma_item_indeed
1096       \fi}
1097
1098    \def\syst_helpers_do_do_process_comma_item_indeed
1099      {\ifx\nexttoken]%
1100         \expandafter\gobbleoneargument
1101       \else
1102         \expandafter\syst_helpers_do_do_do_process_comma_item
1103       \fi}
1104
1105% \else
1106%
1107%     \def\syst_helpers_do_process_comma_item
1108%       {\futureexpandis]\gobbleoneargument\syst_helpers_do_do_do_process_comma_item}
1109%
1110% \fi
1111
1112%D Empty arguments are not processed. Empty items (\type {,,}) however are
1113%D treated. We have to check for the special case \type {[{a,b,c}]}.
1114
1115\protected\def\processcommalist[%
1116  {\futurelet\nexttoken\syst_helpers_do_check_comma_item}
1117
1118\def\syst_helpers_do_check_comma_item
1119  {\ifx\nexttoken]%
1120     \expandafter\gobblethreearguments
1121   \else
1122     \expandafter\syst_helpers_do_process_comma_list
1123   \fi
1124   \relax} % this one preserved the next {}
1125
1126% \def\syst_helpers_do_process_comma_list#1]#2%
1127%   {\global\advance\commalevel \plusone
1128%    \expandafter\def\csname\??nextcommalevel\the\commalevel\endcsname##1,%
1129%      {#2{##1}\syst_helpers_do_process_comma_item}%
1130%    \expandafter\syst_helpers_do_do_process_comma_item\gobbleoneargument#1,]\relax
1131%    \global\advance\commalevel \minusone }
1132
1133\def\syst_helpers_do_process_comma_list#1]#2%
1134  {\global\advance\commalevel \plusone
1135   \expandafter\def\csname\??nextcommalevel\the\commalevel\endcsname##1,%
1136     {#2{##1}\syst_helpers_do_process_comma_item}%
1137   \syst_helpers_do_do_process_comma_item_gobble#1,]\relax
1138   \global\advance\commalevel \minusone }
1139
1140\def\syst_helpers_do_do_process_comma_item_gobble#1{\syst_helpers_do_do_process_comma_item}
1141
1142%D One way of quitting a commalist halfway is:
1143
1144\protected\def\quitcommalist
1145  {\begingroup\let\syst_helpers_do_process_comma_item\syst_helpers_do_quit_comma_list}
1146
1147\def\syst_helpers_do_quit_comma_list#1]%
1148  {\endgroup}
1149
1150\protected\def\quitprevcommalist
1151  {\begingroup\let\syst_helpers_do_process_comma_item\syst_helpers_do_quit_prev_comma_list}
1152
1153\def\syst_helpers_do_quit_prev_comma_list#1]%
1154  {\let\syst_helpers_do_process_comma_item\syst_helpers_do_quit_comma_list}
1155
1156%D The hack we used for checking the next character \type {\doifnextcharelse}
1157%D is also used here.
1158
1159% \ifcase\contextlmtxmode
1160
1161    \let\next\:
1162
1163    \def\:{\syst_helpers_re_do_process_comma_item} % \:not saved ?
1164
1165    \expandafter\def\: {\futurelet\nexttoken\syst_helpers_do_do_process_comma_item}
1166
1167    \let\:\next
1168
1169% \fi
1170
1171%D The previous examples lead to:
1172%D
1173%D \getbuffer
1174
1175%D When a list is saved in a macro, we can use a construction like:
1176%D
1177%D \starttyping
1178%D \expandafter\processcommalist\expandafter[\list]\command
1179%D \stoptyping
1180%D
1181%D Such solutions suit most situations, but we wanted a bit more.
1182%D
1183%D \starttyping
1184%D \processcommacommand[string,\stringset,string]\commando
1185%D \stoptyping
1186%D
1187%D where \type{\stringset} is a predefined set, like:
1188%D
1189%D \starttyping
1190%D \def\first{aap,noot,mies}
1191%D \def\second{laatste}
1192%D
1193%D \processcommacommand[\first]\message
1194%D \processcommacommand[\first,second,third]\message
1195%D \processcommacommand[\first,between,\second]\message
1196%D \stoptyping
1197%D
1198%D Commands that are part of the list are expanded, so the use of this macro has its
1199%D limits.
1200
1201\protected\def\processcommacommand[#1]%
1202  {\normalexpanded{\processcommalist[#1]}}
1203
1204%D The argument to \type{\command} is not delimited. Because we often use \type {[]}
1205%D as delimiters, we also have:
1206%D
1207%D \starttyping
1208%D \processcommalistwithparameters[string,string,...]\command
1209%D \stoptyping
1210%D
1211%D where \type{\command} looks like:
1212%D
1213%D \starttyping
1214%D \def\command[#1]{... #1 ...}
1215%D \stoptyping
1216
1217\protected\def\processcommalistwithparameters[#1]#2%
1218  {\def\syst_helpers_do_process_comma_list_with_parameters##1{#2[##1]}%
1219   \processcommalist[#1]\syst_helpers_do_process_comma_list_with_parameters}
1220
1221%D \macros
1222%D   {startprocesscommalist,startprocesscommacommand}
1223%D
1224%D Two more:
1225
1226\let\syst_helpers_comma_list_step\relax
1227
1228\protected\def\startprocesscommalist[#1]#2\stopprocesscommalist
1229  {\def\syst_helpers_comma_list_step##1{\def\currentcommalistitem{##1}#2}%
1230   \processcommalist[#1]\syst_helpers_comma_list_step}
1231
1232\protected\def\startprocesscommacommand[#1]#2\stopprocesscommacommand
1233  {\def\syst_helpers_comma_list_step##1{\def\currentcommalistitem{##1}#2}%
1234   \normalexpanded{\processcommalist[#1]}\syst_helpers_comma_list_step}
1235
1236\let\stopprocesscommalist   \relax
1237\let\stopprocesscommacommand\relax
1238
1239%D \macros
1240%D   {processaction,
1241%D    processfirstactioninset,
1242%D    processallactionsinset}
1243%D
1244%D \CONTEXT\ makes extensive use of a sort of case or switch command. Depending of
1245%D the presence of one or more provided items, some actions is taken. These macros
1246%D can be nested without problems.
1247%D
1248%D \starttyping
1249%D \processaction           [x]     [a=>\a,b=>\b,c=>\c]
1250%D \processfirstactioninset [x,y,z] [a=>\a,b=>\b,c=>\c]
1251%D \processallactionsinset  [x,y,z] [a=>\a,b=>\b,c=>\c]
1252%D \stoptyping
1253%D
1254%D We can supply both a \type {default} action and an action to be undertaken when
1255%D an \type {unknown} value is met:
1256%D
1257%D \starttyping
1258%D \processallactionsinset
1259%D   [x,y,z]
1260%D   [      a=>\a,
1261%D          b=>\b,
1262%D          c=>\c,
1263%D    default=>\default,
1264%D    unknown=>\unknown{... \commalistelement ...}]
1265%D \stoptyping
1266%D
1267%D When \type {#1} is empty, this macro scans list \type {#2} for the keyword \type
1268%D {default} and executed the related action if present. When \type {#1} is non
1269%D empty and not in the list, the action related to \type {unknown} is executed.
1270%D Both keywords must be at the end of list \type{#2}. Afterwards, the actually
1271%D found keyword is available in \type {\commalistelement}. An advanced example of
1272%D the use of this macro can be found in \PPCHTEX, where we completely rely on \TEX\
1273%D for interpreting user supplied keywords like \type {SB}, \type {SB1..6}, \type
1274%D {SB125} etc.
1275
1276\newcount\processlevel
1277
1278% obsolete: \def\expandactions{\let\expandedaction\edef} \expandactions (see mkii)
1279
1280\protected\def\syst_helpers_do_compare_process_action_a[#1=>#2][#3]%
1281  {\edef\m_syst_string_two{#1}%
1282   \ifx\m_syst_string_two\s!default
1283     \let\commalistelement\empty
1284     #2%
1285   \fi}
1286
1287% met \quitcommalist tot meer dan 25\% sneller
1288
1289\protected\def\syst_helpers_do_compare_process_action_b[#1=>#2][#3]%
1290  {\edef\m_syst_string_two{#1}%
1291   \ifx\m_syst_string_one\m_syst_string_two
1292     \def\commalistelement{#3}%
1293     #2%
1294     \expandafter\quitcommalist
1295   \else\ifx\m_syst_string_two\s!unknown
1296     \def\commalistelement{#3}% beware of loops
1297     #2%
1298   \fi\fi}
1299
1300\protected\def\processaction[#1]#2[%
1301  {\edef\m_syst_string_one{#1}%
1302   \ifx\m_syst_string_one\empty
1303     \let\syst_helpers_do_compare_process_action\syst_helpers_do_compare_process_action_a
1304   \else
1305     \let\syst_helpers_do_compare_process_action\syst_helpers_do_compare_process_action_b
1306   \fi
1307   \edef\syst_helpers_do_process_action##1{\syst_helpers_do_compare_process_action[##1][#1]}% expands #1
1308   \processnextcommalist\relax\relax\syst_helpers_do_process_action[}
1309
1310\protected\def\syst_helpers_do_compare_process_action_c[#1=>#2][#3]%
1311  {\edef\m_syst_string_one{#1}%
1312   \edef\m_syst_string_two{#3}%
1313   \ifx\m_syst_string_one\m_syst_string_two
1314     \def\commalistelement{#3}%
1315     #2%
1316     \expandafter\quitprevcommalist
1317   \else
1318     \edef\m_syst_string_one{#1}%
1319     \ifx\m_syst_string_one\s!unknown
1320       \def\commalistelement{#3}%
1321       #2%
1322     \fi
1323   \fi}
1324
1325\protected\def\processfirstactioninset[#1]%
1326  {\edef\m_syst_string_one{#1}%
1327   \ifx\m_syst_string_one\empty
1328     \expandafter\processaction
1329   \else
1330     \expandafter\syst_helpers_process_first_action_in_set_indeed
1331   \fi
1332   [#1]}
1333
1334\protected\def\syst_helpers_process_first_action_in_set_indeed[#1]#2[#3]%
1335  {\def\syst_helpers_do_process_action##1%
1336     {\def\syst_helpers_do_do_process_action####1{\syst_helpers_do_compare_process_action_c[####1][##1]}%
1337      \processcommalist[#3]\syst_helpers_do_do_process_action}%
1338   \normalexpanded{\processcommalist[#1]}\syst_helpers_do_process_action}
1339
1340\protected\def\syst_helpers_do_compare_process_action_d[#1=>#2][#3]%
1341  {\edef\m_syst_string_one{#1}%
1342   \edef\m_syst_string_two{#3}%
1343   \ifx\m_syst_string_one\m_syst_string_two
1344     \def\commalistelement{#3}%
1345     #2%
1346     \expandafter\quitcommalist
1347   \else
1348     \edef\m_syst_string_one{#1}%
1349     \ifx\m_syst_string_one\s!unknown
1350       \def\commalistelement{#3}%
1351       #2%
1352     \fi
1353   \fi}
1354
1355\installsystemnamespace{nextactionlevel}
1356
1357\protected\def\syst_helpers_do_process_all_actions_in_set
1358  {\csname\??nextactionlevel\the\processlevel\endcsname}
1359
1360\protected\def\processallactionsinset[#1]%
1361  {\edef\m_syst_string_one{#1}%
1362   \ifx\m_syst_string_one\empty
1363     \expandafter\processaction
1364   \else
1365     \expandafter\syst_helpers_process_all_actions_in_set_indeed
1366   \fi
1367   [#1]}
1368
1369\protected\def\syst_helpers_process_all_actions_in_set_indeed[#1]#2[#3]%
1370  {\advance\processlevel \plusone
1371   \expandafter\def\csname\??nextactionlevel\the\processlevel\endcsname##1%
1372     {\def\syst_helpers_do_do_process_action####1{\syst_helpers_do_compare_process_action_d[####1][##1]}%
1373      \processcommalist[#3]\syst_helpers_do_do_process_action}%
1374   \normalexpanded{\processcommalist[#1]}\syst_helpers_do_process_all_actions_in_set
1375   \advance\processlevel\minusone}
1376
1377%D These macros use:
1378
1379\protected\def\processnextcommalist#1#2#3[#4#5]%
1380  {#1%
1381   \let\nexttoken#4%
1382   \global\advance\commalevel \plusone
1383   \expandafter\def\csname\??nextcommalevel\the\commalevel\endcsname##1,%
1384     {#3{##1}\syst_helpers_do_process_comma_item}%
1385   \syst_helpers_do_do_process_comma_item#4#5,]\relax
1386   \global\advance\commalevel\minusone
1387   #2}
1388
1389%D \macros
1390%D   {getfirstcharacter, firstcharacter, remainingcharacters, doiffirstcharacter}
1391%D
1392%D Sometimes the action to be undertaken depends on the next character. This macro
1393%D get this character and puts it in \type {\firstcharacter}.
1394%D
1395%D \starttyping
1396%D \getfirstcharacter {string}
1397%D \stoptyping
1398%D
1399%D A two step expansion is used to prevent problems with complicated arguments, for
1400%D instance arguments that consist of two or more expandable tokens.
1401
1402\let\firstcharacter     \empty
1403\let\remainingcharacters\empty
1404
1405\protected\def\getfirstcharacter     #1{\clf_getfirstcharacter{#1}}
1406\protected\def\doifelsefirstchar   #1#2{\clf_doifelsefirstchar{#1}{#2}}
1407\protected\def\thefirstcharacter     #1{\clf_thefirstcharacter{#1}}
1408\protected\def\theremainingcharacters#1{\clf_theremainingcharacters{#1}}
1409
1410\let\doiffirstcharelse\doifelsefirstchar
1411
1412%D \macros
1413%D   {doifinstringelse, doifincsnameelse}
1414%D
1415%D We can check for the presence of a substring in a given sequence of characters.
1416%D
1417%D \starttyping
1418%D \doifinsetelse {substring} {string} {then ...} {else ...}
1419%D \stoptyping
1420
1421\let\m_syst_sub_string\empty
1422
1423\protected\def\doifelseinstring#1%
1424  {\edef\m_syst_sub_string{#1}% expand #1 here
1425   \ifx\m_syst_sub_string\empty
1426     \expandafter\thirdofthreearguments
1427   \else
1428     \expandafter\syst_helpers_do_if_in_string_else_indeed
1429   \fi}
1430
1431\let\doifinstringelse\doifelseinstring
1432
1433\protected\def\syst_helpers_do_if_in_string_else_indeed#1%
1434  {\syst_helpers_do_if_in_string_else\m_syst_sub_string{#1}%
1435     \expandafter\firstoftwoarguments
1436   \else
1437     \expandafter\secondoftwoarguments
1438   \fi}
1439
1440\protected\def\doifinstring#1%%
1441  {\edef\m_syst_sub_string{#1}% expand #1 here
1442   \ifx\m_syst_sub_string\empty
1443     \expandafter\gobbletwoarguments
1444   \else
1445     \expandafter\syst_helpers_do_if_in_string_indeed
1446   \fi}
1447
1448\protected\def\syst_helpers_do_if_in_string_indeed#1%
1449  {\syst_helpers_do_if_in_string_else\m_syst_sub_string{#1}%
1450     \expandafter\firstofoneargument
1451   \else
1452     \expandafter\gobbleoneargument
1453   \fi}
1454
1455\protected\def\doifnotinstring#1%%
1456  {\edef\m_syst_sub_string{#1}% expand #1 here
1457   \ifx\m_syst_sub_string\empty
1458    %\expandafter\gobbletwoarguments
1459     \expandafter\secondoftwoarguments
1460   \else
1461     \expandafter\syst_helpers_do_if_not_in_string_indeed
1462   \fi}
1463
1464\protected\def\syst_helpers_do_if_not_in_string_indeed#1%
1465  {\syst_helpers_do_if_in_string_else\m_syst_sub_string{#1}%
1466     \expandafter\gobbleoneargument
1467   \else
1468     \expandafter\firstofoneargument
1469   \fi}
1470
1471% replaces prev
1472
1473% \protected\def\syst_helpers_do_if_in_string_else#1#2% ##2 can be {abc}
1474%   {\expandafter\def\expandafter\syst_helpers_do_do_if_in_string_else
1475%      \expandafter##\expandafter1#1##2##3^^^^0004{\unless\if##2@}% expand #1 here
1476%    \expandafter\syst_helpers_do_do_if_in_string_else\normalexpanded{#2#1}@@^^^^0004} % expand #2 here
1477
1478\protected\def\syst_helpers_do_if_in_string_else#1#2% ##2 can be {abc}
1479  {\expandafter\def\expandafter\syst_helpers_do_do_if_in_string_else
1480     \expandafter##\expandafter1#1##2##3^^^^0004{\unless\ifx##2^^^^0003}% expand #1 here
1481   \expandafter\syst_helpers_do_do_if_in_string_else\normalexpanded{#2#1}^^^^0003^^^^0003^^^^0004} % expand #2 here
1482
1483% \protected\def\syst_helpers_do_if_in_string_else#1#2% ##2 can be {abc}
1484%   {\normalexpanded{\def\noexpand\syst_helpers_do_do_if_in_string_else
1485%      ##1#1##2##3}^^^^0004{\unless\ifx##2^^^^0003}% expand #1 here
1486%    \normalexpanded{\noexpand\syst_helpers_do_do_if_in_string_else#2#1}^^^^0003^^^^0003^^^^0004} % expand #2 here
1487
1488%D The next alternative proved to be upto twice as fast on tasks like checking
1489%D reserved words in pretty verbatim typesetting! This is mainly due to the fact
1490%D that passing (expanded) strings is much slower that passing a macro.
1491%D
1492%D \starttyping
1493%D \doifincsnameelse {substring} {\string} {then ...} {else ...}
1494%D \stoptyping
1495%D
1496%D Where \type {\doifinstringelse} does as much expansion as possible, the latter
1497%D alternative does minimal (one level) expansion.
1498
1499% \protected\def\syst_helpers_do_if_in_csname_else#1#2%
1500%   {\def\syst_helpers_do_do_if_in_csname_else##1#1##2##3^^^^0004%
1501%      {\unless\if##2@}%
1502%    \expandafter\syst_helpers_do_do_if_in_csname_else#2#1@@^^^^0004}
1503
1504\protected\def\syst_helpers_do_if_in_csname_else#1#2%
1505  {\def\syst_helpers_do_do_if_in_csname_else##1#1##2##3^^^^0004%
1506     {\unless\ifx##2^^^^0003}%
1507   \expandafter\syst_helpers_do_do_if_in_csname_else#2#1^^^^0003^^^^0003^^^^0004}
1508
1509\protected\def\doifelseincsname#1#2%
1510  {\normalexpanded{\syst_helpers_do_if_in_csname_else{#1}}{#2}%
1511     \expandafter\firstoftwoarguments
1512   \else
1513     \expandafter\secondoftwoarguments
1514   \fi}
1515
1516\let\doifincsnameelse\doifelseincsname
1517
1518%D \macros
1519%D   {doifnumberelse,doifnumber,doifnotnumber}
1520%D
1521%D The next macro executes a command depending of the outcome of a test on numerals.
1522%D This is probably one of the fastest test possible, exept from a less robust
1523%D 10||step \type {\if}||ladder or some tricky \type {\lcode} checking.
1524%D
1525%D \starttyping
1526%D \doifnumberelse {string} {then ...} {else ...}
1527%D \stoptyping
1528%D
1529%D The macro accepts \type {123}, \type {abc}, \type {{}}, \type {\getal} and \type
1530%D {\the\count...}. This macro is a rather dirty one.
1531
1532\def\doifelsenumber#1% does not accept counters (fully expandable)
1533  {\ifcase0\ifcase1#1\or\or\or\or\or\or\or\or\or\else1\fi\space
1534     \expandafter\secondoftwoarguments
1535   \else
1536     \expandafter\firstoftwoarguments
1537   \fi}
1538
1539\def\doifnumber#1%
1540  {\ifcase0\ifcase1#1\or\or\or\or\or\or\or\or\or\else1\fi\space
1541     \expandafter\firstofoneargument
1542   \else
1543     \expandafter\gobbleoneargument
1544   \fi}
1545
1546\def\doifnotnumber#1%
1547  {\ifcase0\ifcase1#1\or\or\or\or\or\or\or\or\or\else1\fi\space
1548     \expandafter\gobbleoneargument
1549   \else
1550     \expandafter\firstofoneargument
1551   \fi}
1552
1553\let\doifnumberelse\doifelsenumber
1554
1555%D \macros
1556%D   {setpercentdimen}
1557%D
1558%D \starttyping
1559%D \scratchdimen=100pt \setpercentdimen\scratchdimen{10\letterpercent}
1560%D \scratchdimen=100pt \setpercentdimen\scratchdimen{5pt}
1561%D \scratchdimen       \percentdimen   \hsize       {10\letterpercent}
1562%D \stoptyping
1563
1564\def\percentdimen#1#2% dimen percentage (with %)
1565  {\dimexpr\clf_percentageof{#2}\dimexpr#1\relax}
1566
1567\protected\def\setpercentdimen#1#2% dimen percentage (with %)
1568  {#1=\clf_percentageof{#2}\dimexpr#1\relax}
1569
1570%D \macros
1571%D   {makerawcommalist,
1572%D    rawdoinsetelse,
1573%D    rawprocesscommalist,
1574%D    rawprocessaction}
1575%D
1576%D Some of the commands mentioned earlier are effective but slow. When one is
1577%D desperately in need of faster alternatives and when the conditions are
1578%D predictable safe, the \type {\raw} alternatives come into focus. A major drawback
1579%D is that they do not take \type {\c!constants} into account, simply because no
1580%D expansion is done. This is no problem with \type {\rawprocesscommalist}, because
1581%D this macro does not compare anything. Expandable macros are permitted as search
1582%D string.
1583%D
1584%D \starttyping
1585%D \makerawcommalist[string,string,...]\stringlist
1586%D \rawdoifelseinset{string}{string,...}{...}{...}
1587%D \rawprocesscommalist[string,string,...]\commando
1588%D \rawprocessaction[x][a=>\a,b=>\b,c=>\c]
1589%D \stoptyping
1590%D
1591%D Spaces embedded in the list, for instance after commas, spoil the search process.
1592%D The gain in speed depends on the length of the argument (the longer the argument,
1593%D the less we gain).
1594
1595\protected\def\makerawcommalist[#1]#2% use \processnext ... here
1596  {\def\syst_helpers_do_make_raw_comma_list##1% we don't expand ##1
1597     {\ifx#2\empty
1598        \def#2{##1}%
1599      \else
1600        \expandafter\def\expandafter#2\expandafter{#2,##1}%
1601      \fi}%
1602   \let#2\empty
1603   \processcommalist[#1]\syst_helpers_do_make_raw_comma_list}
1604
1605\def\syst_helpers_raw_process_comma_item#1,#2% #2 eats up preceding space
1606  {\if]#1\else
1607     \csname\??nextcommalevel\the\commalevel\endcsname{#1}%
1608     \expandafter\syst_helpers_raw_process_comma_item
1609   \fi#2}
1610
1611\protected\def\rawprocesscommalist[#1]#2% accepteert ook [\cs]
1612  {\global\advance\commalevel \plusone
1613   \expandafter\let\csname\??nextcommalevel\the\commalevel\endcsname#2%
1614   \expandafter\syst_helpers_raw_process_comma_item#1,],% \relax
1615   \global\advance\commalevel \minusone }
1616
1617\protected\def\rawprocesscommacommand[#1]% not really needed
1618  {\normalexpanded{\rawprocesscommalist[#1]}}
1619
1620%D Here is one without nesting:
1621
1622\protected\def\syst_helpers_fast_process_comma_item#1,#2% #2 eats up preceding space
1623  {\if]#1\else
1624     \syst_helpers_fast_process_comma_command{#1}%
1625     \expandafter\syst_helpers_fast_process_comma_item
1626   \fi#2}
1627
1628\protected\def\fastprocesscommalist[#1]#2% accepteert ook [\cs]
1629  {\let\syst_helpers_fast_process_comma_command#2%
1630   \expandafter\syst_helpers_fast_process_comma_item#1,],}% \relax
1631
1632\protected\def\fastprocesscommacommand[#1]#2% accepteert ook [\cs]
1633  {\let\syst_helpers_fast_process_comma_command#2%
1634   \normalexpanded{\syst_helpers_fast_process_comma_item#1},],}% \relax
1635
1636% \def\rawdoifelseinset#1#2{\doifinstringelse{,#1,}{,#2,}}
1637% \def\rawdoifinset    #1#2{\doifinstring    {,#1,}{,#2,}}
1638
1639\def\m_syst_two_commas{,,}
1640
1641\protected\def\rawdoifelseinset#1%
1642  {\edef\m_syst_sub_string{,#1,}% expand #1 here
1643   \ifx\m_syst_sub_string\m_syst_two_commas
1644     \expandafter\thirdofthreearguments
1645   \else
1646     \expandafter\syst_helpers_raw_do_if_in_set_else
1647   \fi}
1648
1649\let\rawdoifinsetelse\rawdoifelseinset
1650
1651\protected\def\syst_helpers_raw_do_if_in_set_else#1%
1652  {\syst_helpers_do_if_in_string_else\m_syst_sub_string{,#1,}%
1653     \expandafter\firstoftwoarguments
1654   \else
1655     \expandafter\secondoftwoarguments
1656   \fi}
1657
1658\protected\def\rawdoifinset#1%
1659  {\edef\m_syst_sub_string{,#1,}% expand #1 here
1660   \ifx\m_syst_sub_string\m_syst_two_commas
1661     \expandafter\gobbletwoarguments
1662   \else
1663     \expandafter\syst_helpers_raw_do_if_in_set
1664   \fi}
1665
1666\protected\def\syst_helpers_raw_do_if_in_set#1%%
1667  {\syst_helpers_do_if_in_string_else\m_syst_sub_string{,#1,}%
1668     \expandafter\firstofoneargument
1669   \else
1670     \expandafter\gobbleoneargument
1671   \fi}
1672
1673%D Some more raw material:
1674
1675\def\syst_helpers_do_raw_process_action[#1][#2]%
1676  {\def\syst_helpers_do_do_raw_process_action##1,#1=>##2,##3^^^^0004%
1677     {\if##3@\else
1678        \def\m_syst_helpers_process_action{##2}%
1679      \fi}%
1680   \syst_helpers_do_do_raw_process_action,#2,#1=>,@^^^^0004}
1681
1682\protected\def\rawprocessaction[#1]#2[#3]%
1683  {\edef\m_syst_string_one{#1}%
1684   \edef\m_syst_string_two{undefined}% better \!!undefined
1685   \let\m_syst_helpers_process_action\m_syst_string_two
1686   \ifx\m_syst_string_one\empty
1687     \expandafter\syst_helpers_do_raw_process_action\expandafter[\s!default][#3]%
1688   \else
1689      \expandafter\syst_helpers_do_raw_process_action\expandafter[\m_syst_string_one][#3]%
1690      \ifx\m_syst_helpers_process_action\m_syst_string_two
1691        \expandafter\syst_helpers_do_raw_process_action\expandafter[\s!unknown][#3]%
1692      \fi
1693   \fi
1694   \ifx\m_syst_helpers_process_action\m_syst_string_two
1695   \else
1696     \m_syst_helpers_process_action
1697   \fi}
1698
1699%D When we process the list \type {a,b,c,d,e}, the raw routine takes over 30\% less
1700%D time, when we feed $20+$ character strings we gain about 20\%. Alternatives which
1701%D use \type {\futurelet} perform worse. Part of the speedup is due to the \type
1702%D {\let} and \type {\expandafter} in the test.
1703%D
1704%D \macros
1705%D   {dosetvalue,dosetevalue,dosetgvalue,docopyvalue,doresetvalue,
1706%D    dogetvalue}
1707%D
1708%D When we are going to do assignments, we have to take multi||linguality into account.
1709%D For the moment we keep things simple and single||lingual.
1710%D
1711%D \starttyping
1712%D \dosetvalue   {label}    {variable}   {value}
1713%D \dosetevalue  {label}    {variable}   {value}
1714%D \dosetgvalue  {label}    {variable}   {value}
1715%D \docopyvalue  {to label} {from label} {variable}
1716%D \doresetvalue {label}    {variable}
1717%D \stoptyping
1718%D
1719%D These macros are in fact auxiliary ones and are not meant for use outside the
1720%D assignment macros.
1721
1722\def\dosetvalue#1#2% #3
1723  {\expandafter\def\csname#1#2\endcsname} % {#3}}
1724
1725\def\dosetevalue#1#2% #3
1726  {\expandafter\edef\csname#1#2\endcsname} % {#3}}
1727
1728\def\dosetgvalue#1#2% #3
1729  {\expandafter\gdef\csname#1#2\endcsname} % {#3}}
1730
1731\def\doresetvalue#1#2%
1732  {\expandafter\let\csname#1#2\endcsname\empty}
1733
1734\def\doignorevalue#1#2#3%
1735  {\expandafter\let\csname#1#2\endcsname\empty}
1736
1737\def\docopyvalue#1#2#3%
1738  {\expandafter\def\csname#1#3\endcsname{\csname#2#3\endcsname}}
1739
1740%D Experiment:
1741
1742\edef\m_syst_protected{\detokenize{protected}}
1743
1744\protected\def\aliasmacro#1#2%
1745  {\doifinstring{\m_syst_protected}{\meaning#2}\protected
1746   \def#1{#2}}
1747
1748%D \macros
1749%D   {doassign,undoassign,doassignempty}
1750%D
1751%D Assignments are the backbone of \CONTEXT. Abhorred by the concept of style file
1752%D hacking, we took a considerable effort in building a parameterized system.
1753%D Unfortunately there is a price to pay in terms of speed. Compared to other
1754%D packages and taking the functionality of \CONTEXT\ into account, the total size
1755%D of the format file is still very acceptable. Now how are these assignments done.
1756%D
1757%D Assignments can be realized with:
1758%D
1759%D \starttyping
1760%D \doassign[label][variable=value]
1761%D \undoassign[label][variable=value]
1762%D \stoptyping
1763%D
1764%D and:
1765%D
1766%D \starttyping
1767%D \doassignempty[label][variable=value]
1768%D \stoptyping
1769%D
1770%D Assignments like \type{\doassign} are compatible with:
1771%D
1772%D \starttyping
1773%D \def\labelvariable{value}
1774%D \stoptyping
1775%D
1776%D We do check for the presence of an \type{=} and loudly complain of it's missed. We
1777%D will redefine this macro later on, when a more advanced message mechanism is
1778%D implemented.
1779
1780\protected\def\showassignerror#1#2%
1781  {\writestatus{setup}{missing or ungrouped '=' after '#1' in line #2}}
1782
1783\protected\def\doassignempty[#1][#2=#3]%
1784  {\ifcsname#1#2\endcsname\else\dosetvalue{#1}{#2}{#3}\fi}
1785
1786%D \macros
1787%D   {getparameters,geteparameters,getgparameters,
1788%D    forgetparameters}
1789%D
1790%D Using the assignment commands directly is not our ideal of user friendly interfacing,
1791%D so we take some further steps.
1792%D
1793%D \starttyping
1794%D \getparameters    [label] [...=...,...=...]
1795%D \forgetparameters [label] [...=...,...=...]
1796%D \stoptyping
1797%D
1798%D Again, the label identifies the category a variable belongs to. The second argument
1799%D can be a comma separated list of assignments.
1800%D
1801%D \starttyping
1802%D \getparameters
1803%D   [demo]
1804%D   [alfa=1,
1805%D    beta=2]
1806%D \stoptyping
1807%D
1808%D is equivalent to
1809%D
1810%D \starttyping
1811%D \def\demoalfa{1}
1812%D \def\demobeta{2}
1813%D \stoptyping
1814%D
1815%D
1816%D In the pre||multi||lingual stadium \CONTEXT\ took the next approach. With
1817%D
1818%D \starttyping
1819%D \def\??demo {@@demo}
1820%D \def\!!alfa {alfa}
1821%D \def\!!beta {beta}
1822%D \stoptyping
1823%D
1824%D calling
1825%D
1826%D \starttyping
1827%D \getparameters
1828%D   [\??demo]
1829%D   [\!!alfa=1,
1830%D    \!!beta=2]
1831%D \stoptyping
1832%D
1833%D lead to:
1834%D
1835%D \starttyping
1836%D \def\@@demoalfa{1}
1837%D \def\@@demobeta{2}
1838%D \stoptyping
1839%D
1840%D Because we want to be able to distinguish the \type{!!} pre||tagged user supplied
1841%D variables from internal counterparts, we will introduce a slightly different tag
1842%D in the multi||lingual modules. There we will use \type{c!} or \type{v!},
1843%D depending on the context.
1844%D
1845%D By calling \type{doassign} directly, we save ourselves some argument passing
1846%D and gain some speed. Whatever optimizations we do, this command will always be
1847%D one of the bigger bottlenecks. The alternative \type{\geteparameters} --- it's
1848%D funny to see that this alternative saw the light so lately --- can be used to do
1849%D expanded assigments.
1850
1851\let\currentvalue\empty
1852
1853\protected\def\getparameters   {\dogetparameters\dosetvalue}
1854\protected\def\geteparameters  {\dogetparameters\dosetevalue}
1855\protected\def\getgparameters  {\dogetparameters\dosetgvalue}
1856\protected\def\getxparameters  {\dogetparameters\dosetxvalue}
1857\protected\def\forgetparameters{\dogetparameters\doignorevalue}
1858
1859\let\getexpandedparameters\geteparameters
1860
1861\protected\def\dogetparameters#1[#2]#3[#4%
1862  {\if\noexpand#4]%
1863     \expandafter\gobbleoneargument
1864   \else
1865     \let\setsomevalue#1%
1866     \def\syst_helpers_get_parameters_assign{\syst_helpers_get_parameters_assign_indeed#2}%
1867     \expandafter\syst_helpers_get_parameters
1868   \fi#4}
1869
1870\def\syst_helpers_get_parameters#1]%
1871  {\xprocesscommaitem#1,],^^^^0004}
1872
1873\def\syst_helpers_process_comma_item#1,#2% #2 takes space before ,
1874  {\if,#1,% dirty trick for testing #1=empty
1875     \expandafter\syst_helpers_process_comma_item
1876   \else\if]#1%
1877     \doubleexpandafter\gobbleoneargument
1878   \else
1879     \syst_helpers_get_parameters_assign^^^^0004#1==\empty^^^^0004%
1880     \doubleexpandafter\syst_helpers_process_comma_item
1881   \fi\fi#2}
1882
1883\def\syst_helpers_assign_error#1#2#3%
1884  {\showassignerror{#2}{\the\inputlineno\space(#1)}}
1885
1886\def\syst_helpers_get_parameters_assign_normal#1^^^^0004#2=#3=#4#5^^^^0004%
1887  {\ifx\empty#2\empty
1888     \expandafter\syst_helpers_assign_error
1889   \else\ifx#4\empty
1890     \doubleexpandafter\syst_helpers_assign_error
1891   \else
1892     \doubleexpandafter\setsomevalue
1893   \fi\fi
1894   {#1}{#2}{#3}}
1895
1896\def\syst_helpers_get_parameters_assign_error#1^^^^0004#2=#3=#4#5^^^^0004%
1897  {\ifx\empty#2\empty
1898     \expandafter\syst_helpers_assign_error
1899   \else\ifx#4\empty
1900     \doubleexpandafter\syst_helpers_assign_error
1901   \else
1902     \ifcsname#1#2\endcsname
1903       \expandafter\let\expandafter\currentvalue\csname#1#2\endcsname
1904     \else
1905       \let\currentvalue\empty
1906     \fi
1907     \doubleexpandafter\setsomevalue
1908   \fi\fi
1909   {#1}{#2}{#3}}
1910
1911\let\syst_helpers_get_parameters_assign_indeed\syst_helpers_get_parameters_assign_normal
1912
1913\protected\def\doassign  [#1][#2]{\let\setsomevalue\dosetvalue  \syst_helpers_get_parameters_assign_indeed#1^^^^0004#2==\empty^^^^0004}
1914\protected\def\doeassign [#1][#2]{\let\setsomevalue\dosetevalue \syst_helpers_get_parameters_assign_indeed#1^^^^0004#2==\empty^^^^0004}
1915\protected\def\undoassign[#1][#2]{\let\setsomevalue\doresetvalue\syst_helpers_get_parameters_assign_indeed#1^^^^0004#2==\empty^^^^0004}
1916
1917%D \macros
1918%D   {processassignmentlist,processassignmentcommand,
1919%D    startprocessassignmentlist,startprocessassignmentcommand}
1920%D
1921%D For Wolfgang:
1922%D
1923%D \starttyping
1924%D \def\showpair#1#2{key:#1, value:#2\par}
1925%D \processassignmentlist[a=1,b=2]\showpair
1926%D \stoptyping
1927%D
1928%D We can optimize this one if needed but it's not a core macro so hardly worth the
1929%D trouble and tokens.
1930
1931\protected\def\processassignmentlist[#1]#2% #2 == \command{key}{value]
1932  {\def\syst_helpers_process_assignment_entry##1{#2}% {##2}{##3} % namespace is ignored
1933   \dogetparameters\syst_helpers_process_assignment_entry[][#1]}
1934
1935\protected\def\processassignmentcommand[#1]%
1936  {\normalexpanded{\processassignmentlist[#1]}}
1937
1938\protected\def\startprocessassignmentlist[#1]#2\stopprocessassignmentlist
1939  {\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}%
1940   \processassignmentlist[#1]\currentassignmentlistcommand}
1941
1942\protected\def\startprocessassignmentcommand[#1]#2\stopprocessassignmentcommand
1943  {\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}%
1944   \normalexpanded{\processassignmentlist[#1]}\currentassignmentlistcommand}
1945
1946%D \macros{currentvalue}
1947%D
1948%D Just in case a \type{\getparameter} argument itself ends up inside a \type
1949%D {\write} or other expandable location, our new macro needs a default value.
1950%D
1951%D \starttyping
1952%D \getparameters[xxx][aaa=bbb]\par
1953%D \getparameters[xxx][=bbb]\par
1954%D \getparameters[xxx][aaa=]\par
1955%D \getparameters[xxx][=]\par
1956%D \getparameters[xxx][aaa]\par
1957%D \stoptyping
1958
1959%D \macros
1960%D   {expandparameters}
1961%D
1962%D Example usage:
1963%D
1964%D \startbuffer
1965%D \getparameters[taco][name=taco]
1966%D \convertcommand\taconame\to\ascii \ascii
1967%D \expandparameters \getparameters[taco][name=\currentvalue\space hoekwater]
1968%D \convertcommand\taconame\to\ascii \ascii
1969%D \getparameters[taco][name=\currentvalue\space hoekwater]
1970%D \convertcommand\taconame\to\ascii \ascii
1971%D \stopbuffer
1972%D
1973%D \typebuffer
1974%D \startlines
1975%D \getbuffer
1976%D \stoplines
1977%D
1978%D Here we hook in the code (beware, this is the optimized get **):
1979
1980\def\syst_helpers_get_parameters_normal#1]%
1981  {\syst_helpers_process_comma_item#1,],^^^^0004}
1982
1983\def\syst_helpers_get_parameters_expanded#1]%
1984  {\let\dosetnvalue\setsomevalue
1985   \let\setsomevalue\dosetevalue
1986   \let\syst_helpers_get_parameters_assign_indeed\syst_helpers_get_parameters_assign_error
1987   \let\setsomevalue\dosetevalue
1988   \syst_helpers_process_comma_item#1,],^^^^0004%
1989   \let\syst_helpers_get_parameters_assign_indeed\syst_helpers_get_parameters_assign_normal
1990   \let\setsomevalue\dosetnvalue
1991   \let\syst_helpers_get_parameters\syst_helpers_get_parameters_normal
1992   \let\currentvalue\empty}
1993
1994\let\syst_helpers_get_parameters\syst_helpers_get_parameters_normal % **
1995
1996\protected\def\expandparameters
1997  {\let\syst_helpers_get_parameters\syst_helpers_get_parameters_expanded}
1998
1999%D \macros
2000%D   {getemptyparameters}
2001%D
2002%D Sometimes we explicitly want variables to default to an empty string, so we
2003%D welcome:
2004%D
2005%D \starttyping
2006%D \getemptyparameters [label] [...=...,...=...]
2007%D \stoptyping
2008
2009\protected\def\getemptyparameters[#1]#2[#3]%
2010  {\def\syst_helpers_get_empty_parameters##1{\doassignempty[#1][##1]}%
2011   \processcommalist[#3]\syst_helpers_get_empty_parameters}
2012
2013%D \macros
2014%D   {copyparameters}
2015%D
2016%D Some \CONTEXT\ commands take their default setups from others. All commands that
2017%D are able to provide backgounds or rules around some content, for instance default
2018%D to the standard command for ruled boxes. Is situations like this we can use:
2019%D
2020%D \starttyping
2021%D \copyparameters [to-label] [from-label] [name1,name2,...]
2022%D \stoptyping
2023%D
2024%D For instance
2025%D
2026%D \starttyping
2027%D \copyparameters
2028%D   [internal][external]
2029%D   [alfa,beta]
2030%D \stoptyping
2031%D
2032%D Leads to:
2033%D
2034%D \starttyping
2035%D \def\internalalfa {\externalalfa}
2036%D \def\internalbeta {\externalbeta}
2037%D \stoptyping
2038%D
2039%D By using \type {\docopyvalue} we've prepared this command for use in a
2040%D multi||lingual environment.
2041
2042\protected\def\copyparameters[#1]#2[#3]#4[#5]%
2043  {\doifnot{#1}{#3}
2044     {\def\syst_helpers_copy_parameter{\docopyvalue{#1}{#3}}% ##1
2045      \processcommalist[#5]\syst_helpers_copy_parameter}}
2046
2047%D \macros
2048%D   {ifparameters,checkparameters}
2049%D
2050%D A slightly different one is \type {\checkparameters}, which also checks on the
2051%D presence of a~\type {=}.
2052%D
2053%D The boolean \type {\ifparameters} can be used afterwards. Combining both in one
2054%D \type {\if}||macro would lead to problems with nested \type {\if}'s.
2055%D
2056%D \starttyping
2057%D \checkparameters[argument]
2058%D \stoptyping
2059
2060\newif\ifparameters
2061
2062\def\syst_helpers_check_parameters#1=#2#3^^^^0004%
2063  {\if#2^^^^0003\parametersfalse\else\parameterstrue\fi}
2064
2065\protected\def\checkparameters[#1]%
2066  {\syst_helpers_check_parameters#1=^^^^0003^^^^0003^^^^0004}
2067
2068%D \macros
2069%D   {getfromcommalist,getfromcommacommand,
2070%D    commalistelement,
2071%D    getcommalistsize,getcommacommandsize}
2072%D
2073%D It's possible to get an element from a commalist or a command representing
2074%D a commalist.
2075%D
2076%D \starttyping
2077%D \getfromcommalist    [string] [n]
2078%D \getfromcommacommand [string,\strings,string,...] [n]
2079%D \stoptyping
2080%D
2081%D The difference betwee the two of them is the same as the difference between
2082%D \type {\processcomma...}. The found string is stored in \type
2083%D {\commalistelement}.
2084%D
2085%D We can calculate the size of a comma separated list by using:
2086%D
2087%D \starttyping
2088%D \getcommalistsize    [string,string,...]
2089%D \getcommacommandsize [string,\strings,string,...]
2090%D \stoptyping
2091%D
2092%D Afterwards, the length is available in the macro \type {\commalistsize}
2093%D (not a \COUNTER).
2094
2095\newcount\commalistcounter
2096
2097\def\commalistsize{0}
2098
2099\def\syst_helpers_get_comma_list_size#1%
2100  {\advance\commalistcounter\plusone}
2101
2102\protected\def\getcommalistsize#1]% don't loose [{#1}]
2103  {\commalistcounter\zerocount
2104   \processcommalist#1]\syst_helpers_get_comma_list_size   % was [{#1}]
2105   \edef\commalistsize{\the\commalistcounter}}
2106
2107% \def\getcommacommandsize[#1]%
2108%   {\edef\commacommand{#1}%
2109%    \scratchtoks\expandafter{\expandafter[\commacommand]}%
2110%    \expandafter\getcommalistsize\the\scratchtoks }
2111
2112\protected\def\getcommacommandsize[#1]%
2113  {\normalexpanded{\getcommalistsize[#1]}}
2114
2115\def\syst_helpers_get_from_comma_list#1%
2116  {\advance\commalistcounter \minusone
2117   \ifcase\commalistcounter
2118     \def\commalistelement{#1}%
2119     \expandafter\quitcommalist
2120   \fi}
2121
2122\protected\def\getfromcommalist[#1]#2[#3]%
2123  {\let\commalistelement\empty
2124   \commalistcounter#3\relax
2125   \processcommalist[#1]\syst_helpers_get_from_comma_list}
2126
2127\protected\def\getfromcommacommand[#1]%
2128  {\normalexpanded{\getfromcommalist[#1]}}
2129
2130%D Watertight (and efficient) solutions are hard to find, due to the handling of
2131%D braces during parameters passing and scanning. Nevertheless:
2132%D
2133%D \startbuffer
2134%D \def\dosomething#1{(#1=\commalistsize) }
2135%D
2136%D \getcommalistsize [\hbox{$a,b,c,d,e,f$}] \dosomething 1
2137%D \getcommalistsize [{a,b,c,d,e,f}]        \dosomething 1
2138%D \getcommalistsize [{a,b,c},d,e,f]        \dosomething 4
2139%D \getcommalistsize [a,b,{c,d,e},f]        \dosomething 4
2140%D \getcommalistsize [a{b,c},d,e,f]         \dosomething 4
2141%D \getcommalistsize [{a,b}c,d,e,f]         \dosomething 4
2142%D \getcommalistsize []                     \dosomething 0
2143%D \getcommalistsize [{[}]                  \dosomething 1
2144%D \stopbuffer
2145%D
2146%D \typebuffer
2147%D
2148%D reports:
2149%D
2150%D \getbuffer
2151
2152%D \macros
2153%D   {dogetcommalistelement,dogetcommacommandelement}
2154%D
2155%D For low level (fast) purposes, we can also use the next alternative, which can
2156%D handle 8~elements at most.
2157%D
2158%D \starttyping
2159%D \dogetcommalistelement1\from a,b,c\to\commalistelement
2160%D \stoptyping
2161
2162\def\syst_helpers_get_comma_list_element#1\from#2,#3,#4,#5,#6,#7,#8\to#9%
2163  {\edef#9{\ifcase#1\relax\or#2\or#3\or#4\or#5\or#6\or#7\or#8\fi}}
2164
2165\def\dogetcommacommandelement#1\from#2\to%
2166  {\expandafter\syst_helpers_get_comma_list_element\expandafter#1\expandafter\from#2,,,,,,\to}
2167
2168%D \macros
2169%D   {dosingleargument,dodoubleargument,dotripleargument,
2170%D    doquadrupleargument,doquintupleargument,dosixtupleargument,
2171%D    doseventupleargument}
2172%D
2173%D When working with delimited arguments, spaces and lineendings can interfere. The
2174%D next set of macros uses \TEX' internal scanner for grabbing everything between
2175%D arguments. Forgive me the funny names.
2176%D
2177%D \starttyping
2178%D \dosingleargument\commando    = \commando[#1]
2179%D \dodoubleargument\commando    = \commando[#1][#2]
2180%D \dotripleargument\commando    = \commando[#1][#2][#3]
2181%D \doquadrupleargument\commando = \commando[#1][#2][#3][#4]
2182%D \doquintupleargument\commando = \commando[#1][#2][#3][#4][#5]
2183%D \dosixtupleargument\commando  = \commando[#1][#2][#3][#4][#5][#6]
2184%D \doseventupleargument\command = \commando[#1][#2][#3][#4][#5][#6][#7]
2185%D \stoptyping
2186%D
2187%D These macros are used in the following way:
2188%D
2189%D \starttyping
2190%D \def\dosetupsomething[#1][#2]%
2191%D   {... #1 ... #2 ...}
2192%D
2193%D \protected\def\setupsomething
2194%D   {\dodoubleargument\dosetupsomething}
2195%D \stoptyping
2196%D
2197%D The implementation can be surprisingly simple and needs no
2198%D further explanation, like:
2199%D
2200%D \starttyping
2201%D \def\dosingleargument#1[#2]%
2202%D   {#1[#2]}
2203%D \def\dotripleargument#1[#2]#3[#4]#5[#6]%
2204%D   {#1[#2][#4][#6]}
2205%D \def\doquintupleargument#1%
2206%D   {\def\dodoquintupleargument[##1]##2[##3]##4[##5]##6[##7]##8[##9]%
2207%D      {#1[##1][##3][##5][##7][##9]}%
2208%D    \dodoquintupleargument}
2209%D \stoptyping
2210%D
2211%D Because \TEX\ accepts 9~arguments at most, we have to use two||step solution when
2212%D getting five or more arguments.
2213%D
2214%D When developing more and more of the real \CONTEXT, we started using some
2215%D alternatives that provided empty arguments (in fact optional ones) whenever the
2216%D user failed to supply them. Because this more complicated macros enable us to do
2217%D some checking, we reimplemented the non||empty ones.
2218
2219% no longer a message:
2220%
2221% \protected\def\dosingleargument    {\let\expectedarguments\plusone   \dosingleempty    }
2222% \protected\def\dodoubleargument    {\let\expectedarguments\plustwo   \dodoubleempty    }
2223% \protected\def\dotripleargument    {\let\expectedarguments\plusthree \dotripleempty    }
2224% \protected\def\doquadrupleargument {\let\expectedarguments\plusfour  \doquadrupleempty }
2225% \protected\def\doquintupleargument {\let\expectedarguments\plusfive  \doquintupleempty }
2226% \protected\def\dosixtupleargument  {\let\expectedarguments\plussix   \dosixtupleempty  }
2227% \protected\def\doseventupleargument{\let\expectedarguments\plusseven \doseventupleempty}
2228
2229%D \macros
2230%D   {iffirstagument,ifsecondargument,ifthirdargument,
2231%D    iffourthargument,iffifthargument,ifsixthargument,
2232%D    ifseventhargument}
2233%D
2234%D We use some signals for telling the calling macros if all wanted arguments are
2235%D indeed supplied by the user.
2236
2237\newif\iffirstargument
2238\newif\ifsecondargument
2239\newif\ifthirdargument
2240\newif\iffourthargument
2241\newif\iffifthargument
2242\newif\ifsixthargument
2243\newif\ifseventhargument
2244
2245%D \macros
2246%D   {dosingleempty,dodoubleempty,dotripleempty,
2247%D    doquadrupleempty,doquintupleempty,dosixtupeempty,
2248%D    doseventupleempty}
2249%D
2250%D The empty argument supplying macros mentioned before, look like:
2251%D
2252%D \starttyping
2253%D \dosingleempty    \command
2254%D \dodoubleempty    \command
2255%D \dotripleempty    \command
2256%D \doquadrupleempty \command
2257%D \doquintupleempty \command
2258%D \dosixtuple_empty  \command
2259%D \doseventupleempty\command
2260%D \stoptyping
2261%D
2262%D So \type{\dodoubleempty} leads to:
2263%D
2264%D \starttyping
2265%D \command[#1][#2]
2266%D \command[#1][]
2267%D \command[][]
2268%D \stoptyping
2269%D
2270%D Depending of the generousity of the user. Afterwards one can use the \type
2271%D {\if...argument} boolean. For novice: watch the stepwise doubling of \type {#}'s.
2272
2273% \setnewconstant\noexpectedarguments\zerocount
2274% \setnewconstant\expectedarguments  \zerocount
2275%
2276% \protected\def\showargumenterror#1#2%
2277%   {\writestatus{system}{\number#1 argument(s) expected in line #2}}
2278%
2279% \protected\def\syst_helpers_argument_error
2280%   {\ifnum\expectedarguments>\noexpectedarguments
2281%      \showargumenterror{\number\expectedarguments}{\number\inputlineno}%
2282%    \fi
2283%    \syst_helpers_argument_reset}
2284%
2285% \protected\def\syst_helpers_argument_reset
2286%   {\let\expectedarguments\noexpectedarguments}
2287
2288% \def\test[#1]{(#1)}
2289%
2290% \dosingleempty\test[]   xxx\par
2291% \dosingleempty\test     xxx\par
2292%
2293% \def\test[#1][#2]{(#1,#2)}
2294%
2295% \dodoubleempty\test[][] xxx\par
2296% \dodoubleempty\test[]   xxx\par
2297% \dodoubleempty\test     xxx\par
2298%
2299% \def\test[#1][#2][#3]{(#1,#2,#3)}
2300%
2301% \dotripleempty\test[][][] xxx\par
2302% \dotripleempty\test[][]   xxx\par
2303% \dotripleempty\test[]     xxx\par
2304% \dotripleempty\test       xxx\par
2305
2306%D Common:
2307
2308\newtoks\t_syst_aux
2309
2310% \def\syst_helpers_empty_spaced_six  {\expandafter\m_syst_aux_do\the\t_syst_aux[][][][][][] }
2311% \def\syst_helpers_empty_normal_six  {\expandafter\m_syst_aux_do\the\t_syst_aux[][][][][][]}
2312% \def\syst_helpers_empty_spaced_five {\expandafter\m_syst_aux_do\the\t_syst_aux[][][][][] }
2313% \def\syst_helpers_empty_normal_five {\expandafter\m_syst_aux_do\the\t_syst_aux[][][][][]}
2314% \def\syst_helpers_empty_spaced_four {\expandafter\m_syst_aux_do\the\t_syst_aux[][][][] }
2315% \def\syst_helpers_empty_normal_four {\expandafter\m_syst_aux_do\the\t_syst_aux[][][][]}
2316% \def\syst_helpers_empty_spaced_three{\expandafter\m_syst_aux_do\the\t_syst_aux[][][] }
2317% \def\syst_helpers_empty_normal_three{\expandafter\m_syst_aux_do\the\t_syst_aux[][][]}
2318% \def\syst_helpers_empty_spaced_two  {\expandafter\m_syst_aux_do\the\t_syst_aux[][] }
2319% \def\syst_helpers_empty_normal_two  {\expandafter\m_syst_aux_do\the\t_syst_aux[][]}
2320% \def\syst_helpers_empty_spaced_one  {\expandafter\m_syst_aux_do\the\t_syst_aux[] }
2321% \def\syst_helpers_empty_normal_one  {\expandafter\m_syst_aux_do\the\t_syst_aux[]}
2322%
2323% \def\syst_helpers_single_empty_one_yes      {\firstargumenttrue              \m_syst_aux_do}
2324% \def\syst_helpers_double_empty_two_yes      {\secondargumenttrue \expandafter\m_syst_aux_do\the\t_syst_aux}
2325% \def\syst_helpers_triple_empty_three_yes    {\thirdargumenttrue  \expandafter\m_syst_aux_do\the\t_syst_aux}
2326% \def\syst_helpers_quadruple_empty_four_yes  {\fourthargumenttrue \expandafter\m_syst_aux_do\the\t_syst_aux}
2327% \def\syst_helpers_quintuple_empty_five_yes  {\fifthargumenttrue  \expandafter\m_syst_aux_do\the\t_syst_aux}
2328% \def\syst_helpers_sixtuple_empty_six_yes    {\sixthargumenttrue  \expandafter\m_syst_aux_do\the\t_syst_aux}
2329% \def\syst_helpers_seventuple_empty_seven_yes{\seventhargumenttrue\expandafter\m_syst_aux_do\the\t_syst_aux}
2330%
2331% with
2332%
2333% \protected\def\dodoubleempty#1%
2334%   {\syst_helpers_argument_reset
2335%    \let\m_syst_aux_do#1% alias
2336%    \let\m_syst_action_yes\syst_helpers_double_empty_one_yes
2337%    \let\m_syst_action_nop\syst_helpers_double_empty_one_nop
2338%    \let\if_next_blank_space_token\iffalse
2339%    \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2340%
2341% \def\syst_helpers_double_empty_one_yes[#1]%
2342%   {\firstargumenttrue
2343%    \t_syst_aux{[{#1}]}% assignment
2344%    \let\m_syst_action_yes\syst_helpers_double_empty_two_yes
2345%    \let\m_syst_action_nop\syst_helpers_double_empty_two_nop
2346%    \let\if_next_blank_space_token\iffalse
2347%    \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2348%
2349% But we use this as it keeps the original name visible:
2350
2351\def\syst_helpers_empty_spaced_six  {\the\t_syst_aux[][][][][][] }
2352\def\syst_helpers_empty_normal_six  {\the\t_syst_aux[][][][][][]}
2353\def\syst_helpers_empty_spaced_five {\the\t_syst_aux[][][][][] }
2354\def\syst_helpers_empty_normal_five {\the\t_syst_aux[][][][][]}
2355\def\syst_helpers_empty_spaced_four {\the\t_syst_aux[][][][] }
2356\def\syst_helpers_empty_normal_four {\the\t_syst_aux[][][][]}
2357\def\syst_helpers_empty_spaced_three{\the\t_syst_aux[][][] }
2358\def\syst_helpers_empty_normal_three{\the\t_syst_aux[][][]}
2359\def\syst_helpers_empty_spaced_two  {\the\t_syst_aux[][] }
2360\def\syst_helpers_empty_normal_two  {\the\t_syst_aux[][]}
2361\def\syst_helpers_empty_spaced_one  {\the\t_syst_aux[] }
2362\def\syst_helpers_empty_normal_one  {\the\t_syst_aux[]}
2363
2364\def\syst_helpers_single_empty_one_yes      {\firstargumenttrue  \the\t_syst_aux}
2365\def\syst_helpers_double_empty_two_yes      {\secondargumenttrue \the\t_syst_aux}
2366\def\syst_helpers_triple_empty_three_yes    {\thirdargumenttrue  \the\t_syst_aux}
2367\def\syst_helpers_quadruple_empty_four_yes  {\fourthargumenttrue \the\t_syst_aux}
2368\def\syst_helpers_quintuple_empty_five_yes  {\fifthargumenttrue  \the\t_syst_aux}
2369\def\syst_helpers_sixtuple_empty_six_yes    {\sixthargumenttrue  \the\t_syst_aux}
2370\def\syst_helpers_seventuple_empty_seven_yes{\seventhargumenttrue\the\t_syst_aux}
2371
2372%D Single:
2373
2374% \protected\def\dosingleempty#1%
2375%   {\syst_helpers_argument_reset
2376%    \doifelsenextoptional
2377%      {\firstargumenttrue#1}%
2378%      {\syst_helpers_single_empty_one_nop#1}}
2379%
2380% \def\syst_helpers_single_empty_one_nop#1%
2381%   {\firstargumentfalse
2382%    #1[]}
2383
2384\protected\def\dosingleempty#1%
2385  {%syst_helpers_argument_reset
2386   \t_syst_aux{#1}%
2387   \let\m_syst_action_yes\syst_helpers_single_empty_one_yes
2388   \let\m_syst_action_nop\syst_helpers_single_empty_one_nop
2389   \let\if_next_blank_space_token\iffalse
2390   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2391
2392\def\syst_helpers_single_empty_one_nop
2393  {\firstargumentfalse
2394   \the\t_syst_aux[]}
2395
2396%D Double
2397
2398% \protected\def\dodoubleempty#1%
2399%   {\syst_helpers_argument_reset
2400%    \doifelsenextoptional
2401%      {\syst_helpers_double_empty_one_yes#1}%
2402%      {\syst_helpers_double_empty_one_nop#1}}
2403%
2404% \def\syst_helpers_double_empty_one_yes#1[#2]%
2405%   {\firstargumenttrue
2406%    \doifelsenextoptional
2407%      {\secondargumenttrue#1[{#2}]}%
2408%      {\syst_helpers_double_empty_two_nop#1{#2}}}
2409%
2410% \def\syst_helpers_double_empty_one_nop#1%
2411%   {\firstargumentfalse
2412%    \secondargumentfalse
2413%    #1[][]}
2414%
2415% \def\syst_helpers_double_empty_two_nop
2416%   {\secondargumentfalse
2417%    \if_next_blank_space_token
2418%      \expandafter\syst_helpers_double_empty_one_spaced
2419%    \else
2420%      \expandafter\syst_helpers_double_empty_one_normal
2421%    \fi}
2422%
2423% \def\syst_helpers_double_empty_one_spaced#1#2{#1[{#2}][] }
2424% \def\syst_helpers_double_empty_one_normal#1#2{#1[{#2}][]}
2425
2426\protected\def\dodoubleempty#1%
2427  {%syst_helpers_argument_reset
2428   \t_syst_aux{#1}%
2429   \let\m_syst_action_yes\syst_helpers_double_empty_one_yes
2430   \let\m_syst_action_nop\syst_helpers_double_empty_one_nop
2431   \let\if_next_blank_space_token\iffalse
2432   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2433
2434\def\syst_helpers_double_empty_one_yes[#1]%
2435  {\firstargumenttrue
2436   \toksapp\t_syst_aux{[{#1}]}%
2437   \let\m_syst_action_yes\syst_helpers_double_empty_two_yes
2438   \let\m_syst_action_nop\syst_helpers_double_empty_two_nop
2439   \let\if_next_blank_space_token\iffalse
2440   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2441
2442\def\syst_helpers_double_empty_one_nop
2443  {\firstargumentfalse
2444   \secondargumentfalse
2445   \the\t_syst_aux[][]}
2446
2447\def\syst_helpers_double_empty_two_nop
2448  {\secondargumentfalse
2449   \if_next_blank_space_token
2450     \expandafter\syst_helpers_empty_spaced_one
2451   \else
2452     \expandafter\syst_helpers_empty_normal_one
2453   \fi}
2454
2455% Triple
2456
2457% \protected\def\dotripleempty#1%
2458%   {\syst_helpers_argument_reset
2459%    \doifelsenextoptional
2460%      {\syst_helpers_triple_empty_one_yes#1}%
2461%      {\syst_helpers_triple_empty_one_nop#1}}
2462%
2463% \def\syst_helpers_triple_empty_one_yes#1[#2]%
2464%   {\firstargumenttrue
2465%    \doifelsenextoptional
2466%      {\syst_helpers_triple_empty_two_yes#1{#2}}%
2467%      {\syst_helpers_triple_empty_two_nop#1{#2}}}
2468%
2469% \def\syst_helpers_triple_empty_two_yes#1#2[#3]%
2470%   {\secondargumenttrue
2471%    \doifelsenextoptional
2472%      {\thirdargumenttrue#1[{#2}][{#3}]}%
2473%      {\syst_helpers_triple_empty_three_nop#1{#2}{#3}}}
2474%
2475% \def\syst_helpers_triple_empty_one_nop#1%
2476%   {\firstargumentfalse
2477%    \secondargumentfalse
2478%    \thirdargumentfalse
2479%    #1[][][]}
2480%
2481% \def\syst_helpers_triple_empty_two_nop
2482%   {\secondargumentfalse
2483%    \thirdargumentfalse
2484%    \if_next_blank_space_token
2485%      \expandafter\syst_helpers_triple_empty_two_spaced
2486%    \else
2487%      \expandafter\syst_helpers_triple_empty_two_normal
2488%    \fi}
2489%
2490% \def\syst_helpers_triple_empty_three_nop
2491%   {\thirdargumentfalse
2492%    \if_next_blank_space_token
2493%      \expandafter\syst_helpers_triple_empty_three_spaced
2494%    \else
2495%      \expandafter\syst_helpers_triple_empty_three_normal
2496%    \fi}
2497%
2498% \def\syst_helpers_triple_empty_two_spaced  #1#2{#1[{#2}][][] }
2499% \def\syst_helpers_triple_empty_two_normal  #1#2{#1[{#2}][][]}
2500% \def\syst_helpers_triple_empty_three_spaced#1#2#3{#1[{#2}][{#3}][] }
2501% \def\syst_helpers_triple_empty_three_normal#1#2#3{#1[{#2}][{#3}][]}
2502
2503\protected\def\dotripleempty#1%
2504  {%syst_helpers_argument_reset
2505   \t_syst_aux{#1}%
2506   \let\m_syst_action_yes\syst_helpers_triple_empty_one_yes
2507   \let\m_syst_action_nop\syst_helpers_triple_empty_one_nop
2508   \let\if_next_blank_space_token\iffalse
2509   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2510
2511\def\syst_helpers_triple_empty_one_yes[#1]%
2512  {\firstargumenttrue
2513   \toksapp\t_syst_aux{[{#1}]}%
2514   \let\m_syst_action_yes\syst_helpers_triple_empty_two_yes
2515   \let\m_syst_action_nop\syst_helpers_triple_empty_two_nop
2516   \let\if_next_blank_space_token\iffalse
2517   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2518
2519\def\syst_helpers_triple_empty_two_yes[#1]%
2520  {\secondargumenttrue
2521   \toksapp\t_syst_aux{[{#1}]}%
2522   \let\m_syst_action_yes\syst_helpers_triple_empty_three_yes
2523   \let\m_syst_action_nop\syst_helpers_triple_empty_three_nop
2524   \let\if_next_blank_space_token\iffalse
2525   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2526
2527\def\syst_helpers_triple_empty_one_nop
2528  {\firstargumentfalse
2529   \secondargumentfalse
2530   \thirdargumentfalse
2531   \the\t_syst_aux[][][]}
2532
2533\def\syst_helpers_triple_empty_two_nop
2534  {\secondargumentfalse
2535   \thirdargumentfalse
2536   \if_next_blank_space_token
2537     \expandafter\syst_helpers_empty_spaced_two
2538   \else
2539     \expandafter\syst_helpers_empty_normal_two
2540   \fi}
2541
2542\def\syst_helpers_triple_empty_three_nop
2543  {\thirdargumentfalse
2544   \if_next_blank_space_token
2545     \expandafter\syst_helpers_empty_spaced_one
2546   \else
2547     \expandafter\syst_helpers_empty_normal_one
2548   \fi}
2549
2550%D Quadruple:
2551
2552% \protected\def\doquadrupleempty#1%
2553%   {\syst_helpers_argument_reset
2554%    \doifelsenextoptional
2555%      {\syst_helpers_quadruple_empty_one_yes#1}%
2556%      {\syst_helpers_quadruple_empty_one_nop#1}}
2557%
2558% \def\syst_helpers_quadruple_empty_one_yes#1[#2]%
2559%   {\firstargumenttrue
2560%    \doifelsenextoptional
2561%      {\syst_helpers_quadruple_empty_two_yes#1{#2}}%
2562%      {\syst_helpers_quadruple_empty_two_nop#1{#2}}}
2563%
2564% \def\syst_helpers_quadruple_empty_two_yes#1#2[#3]%
2565%   {\secondargumenttrue
2566%    \doifelsenextoptional
2567%      {\syst_helpers_quadruple_empty_three_yes#1{#2}{#3}}%
2568%      {\syst_helpers_quadruple_empty_three_nop#1{#2}{#3}}}
2569%
2570% \def\syst_helpers_quadruple_empty_three_yes#1#2#3[#4]%
2571%   {\thirdargumenttrue
2572%    \doifelsenextoptional
2573%      {\fourthargumenttrue#1[{#2}][{#3}][{#4}]}%
2574%      {\syst_helpers_quadruple_empty_four_nop#1{#2}{#3}{#4}}}
2575%
2576% \def\syst_helpers_quadruple_empty_one_nop#1%
2577%   {\firstargumentfalse
2578%    \secondargumentfalse
2579%    \thirdargumentfalse
2580%    \fourthargumentfalse
2581%    #1[][][][]}
2582%
2583% \def\syst_helpers_quadruple_empty_two_nop
2584%   {\secondargumentfalse
2585%    \thirdargumentfalse
2586%    \fourthargumentfalse
2587%    \if_next_blank_space_token
2588%      \expandafter\syst_helpers_quadruple_empty_two_spaced
2589%    \else
2590%      \expandafter\syst_helpers_quadruple_empty_two_normal
2591%    \fi}
2592%
2593% \def\syst_helpers_quadruple_empty_three_nop
2594%   {\thirdargumentfalse
2595%    \fourthargumentfalse
2596%    \if_next_blank_space_token
2597%      \expandafter\syst_helpers_quadruple_empty_three_spaced
2598%    \else
2599%      \expandafter\syst_helpers_quadruple_empty_three_normal
2600%    \fi}
2601%
2602% \def\syst_helpers_quadruple_empty_four_nop
2603%   {\fourthargumentfalse
2604%    \if_next_blank_space_token
2605%      \expandafter\syst_helpers_quadruple_empty_four_spaced
2606%    \else
2607%      \expandafter\syst_helpers_quadruple_empty_four_normal
2608%    \fi}
2609%
2610% \def\syst_helpers_quadruple_empty_two_spaced      #1#2{#1[{#2}][][][] }
2611% \def\syst_helpers_quadruple_empty_two_normal      #1#2{#1[{#2}][][][]}
2612% \def\syst_helpers_quadruple_empty_three_spaced  #1#2#3{#1[{#2}][{#3}][][] }
2613% \def\syst_helpers_quadruple_empty_three_normal  #1#2#3{#1[{#2}][{#3}][][]}
2614% \def\syst_helpers_quadruple_empty_four_spaced #1#2#3#4{#1[{#2}][{#3}][{#4}][] }
2615% \def\syst_helpers_quadruple_empty_four_normal #1#2#3#4{#1[{#2}][{#3}][{#4}][]}
2616
2617\protected\def\doquadrupleempty#1%
2618  {%syst_helpers_argument_reset
2619   \t_syst_aux{#1}%
2620   \let\m_syst_action_yes\syst_helpers_quadruple_empty_one_yes
2621   \let\m_syst_action_nop\syst_helpers_quadruple_empty_one_nop
2622   \let\if_next_blank_space_token\iffalse
2623   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2624
2625\def\syst_helpers_quadruple_empty_one_yes[#1]%
2626  {\firstargumenttrue
2627   \toksapp\t_syst_aux{[{#1}]}%
2628   \let\m_syst_action_yes\syst_helpers_quadruple_empty_two_yes
2629   \let\m_syst_action_nop\syst_helpers_quadruple_empty_two_nop
2630   \let\if_next_blank_space_token\iffalse
2631   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2632
2633\def\syst_helpers_quadruple_empty_two_yes[#1]%
2634  {\secondargumenttrue
2635   \toksapp\t_syst_aux{[{#1}]}%
2636   \let\m_syst_action_yes\syst_helpers_quadruple_empty_three_yes
2637   \let\m_syst_action_nop\syst_helpers_quadruple_empty_three_nop
2638   \let\if_next_blank_space_token\iffalse
2639   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2640
2641\def\syst_helpers_quadruple_empty_three_yes[#1]%
2642  {\thirdargumenttrue
2643   \toksapp\t_syst_aux{[{#1}]}%
2644   \let\m_syst_action_yes\syst_helpers_quadruple_empty_four_yes
2645   \let\m_syst_action_nop\syst_helpers_quadruple_empty_four_nop
2646   \let\if_next_blank_space_token\iffalse
2647   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2648
2649\def\syst_helpers_quadruple_empty_one_nop
2650  {\firstargumentfalse
2651   \secondargumentfalse
2652   \thirdargumentfalse
2653   \fourthargumentfalse
2654   \the\t_syst_aux[][][][]}
2655
2656\def\syst_helpers_quadruple_empty_two_nop
2657  {\secondargumentfalse
2658   \thirdargumentfalse
2659   \fourthargumentfalse
2660   \if_next_blank_space_token
2661     \expandafter\syst_helpers_empty_spaced_three
2662   \else
2663     \expandafter\syst_helpers_empty_normal_three
2664   \fi}
2665
2666\def\syst_helpers_quadruple_empty_three_nop
2667  {\thirdargumentfalse
2668   \fourthargumentfalse
2669   \if_next_blank_space_token
2670     \expandafter\syst_helpers_empty_spaced_two
2671   \else
2672     \expandafter\syst_helpers_empty_normal_two
2673   \fi}
2674
2675\def\syst_helpers_quadruple_empty_four_nop
2676  {\fourthargumentfalse
2677   \if_next_blank_space_token
2678     \expandafter\syst_helpers_empty_spaced_one
2679   \else
2680     \expandafter\syst_helpers_empty_normal_one
2681   \fi}
2682
2683%D Quintuple:
2684
2685% \protected\def\doquintupleempty#1%
2686%   {\syst_helpers_argument_reset
2687%    \doifelsenextoptional
2688%      {\syst_helpers_quintuple_empty_one_yes#1}%
2689%      {\syst_helpers_quintuple_empty_one_nop#1}}
2690%
2691% \def\syst_helpers_quintuple_empty_one_yes#1[#2]%
2692%   {\firstargumenttrue
2693%    \doifelsenextoptional
2694%      {\syst_helpers_quintuple_empty_two_yes#1{#2}}%
2695%      {\syst_helpers_quintuple_empty_two_nop#1{#2}}}
2696%
2697% \def\syst_helpers_quintuple_empty_two_yes#1#2[#3]%
2698%   {\secondargumenttrue
2699%    \doifelsenextoptional
2700%      {\syst_helpers_quintuple_empty_three_yes#1{#2}{#3}}%
2701%      {\syst_helpers_quintuple_empty_three_nop#1{#2}{#3}}}
2702%
2703% \def\syst_helpers_quintuple_empty_three_yes#1#2#3[#4]%
2704%   {\thirdargumenttrue
2705%    \doifelsenextoptional
2706%      {\syst_helpers_quintuple_empty_four_yes#1{#2}{#3}{#4}}%
2707%      {\syst_helpers_quintuple_empty_four_nop#1{#2}{#3}{#4}}}
2708%
2709% \def\syst_helpers_quintuple_empty_four_yes#1#2#3#4[#5]%
2710%   {\fourthargumenttrue
2711%    \doifelsenextoptional
2712%      {\fifthargumenttrue#1[{#2}][{#3}][{#4}][{#5}]}%
2713%      {\syst_helpers_quintuple_empty_five_nop#1{#2}{#3}{#4}{#5}}}
2714%
2715% \def\syst_helpers_quintuple_empty_one_nop#1%
2716%   {\firstargumentfalse
2717%    \secondargumentfalse
2718%    \thirdargumentfalse
2719%    \fourthargumentfalse
2720%    \fifthargumentfalse
2721%    #1[][][][][]}
2722%
2723% \def\syst_helpers_quintuple_empty_two_nop
2724%   {\secondargumentfalse
2725%    \thirdargumentfalse
2726%    \fourthargumentfalse
2727%    \fifthargumentfalse
2728%    \if_next_blank_space_token
2729%      \expandafter\syst_helpers_quintuple_empty_two_spaced
2730%    \else
2731%      \expandafter\syst_helpers_quintuple_empty_two_normal
2732%    \fi}
2733%
2734% \def\syst_helpers_quintuple_empty_three_nop
2735%   {\thirdargumentfalse
2736%    \fourthargumentfalse
2737%    \fifthargumentfalse
2738%    \if_next_blank_space_token
2739%      \expandafter\syst_helpers_quintuple_empty_three_spaced
2740%    \else
2741%      \expandafter\syst_helpers_quintuple_empty_three_normal
2742%    \fi}
2743%
2744% \def\syst_helpers_quintuple_empty_four_nop
2745%   {\fourthargumentfalse
2746%    \fifthargumentfalse
2747%    \if_next_blank_space_token
2748%      \expandafter\syst_helpers_quintuple_empty_four_spaced
2749%    \else
2750%      \expandafter\syst_helpers_quintuple_empty_four_normal
2751%    \fi}
2752%
2753% \def\syst_helpers_quintuple_empty_five_nop
2754%   {\fifthargumentfalse
2755%    \if_next_blank_space_token
2756%      \expandafter\syst_helpers_quintuple_empty_five_spaced
2757%    \else
2758%      \expandafter\syst_helpers_quintuple_empty_five_normal
2759%    \fi}
2760%
2761% \def\syst_helpers_quintuple_empty_two_spaced        #1#2{#1[{#2}][][][][] }
2762% \def\syst_helpers_quintuple_empty_two_normal        #1#2{#1[{#2}][][][][]}
2763% \def\syst_helpers_quintuple_empty_three_spaced    #1#2#3{#1[{#2}][{#3}][][][] }
2764% \def\syst_helpers_quintuple_empty_three_normal    #1#2#3{#1[{#2}][{#3}][][][]}
2765% \def\syst_helpers_quintuple_empty_four_spaced   #1#2#3#4{#1[{#2}][{#3}][{#4}][][] }
2766% \def\syst_helpers_quintuple_empty_four_normal   #1#2#3#4{#1[{#2}][{#3}][{#4}][][]}
2767% \def\syst_helpers_quintuple_empty_five_spaced #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][] }
2768% \def\syst_helpers_quintuple_empty_five_normal #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][]}
2769
2770\protected\def\doquintupleempty#1%
2771  {%syst_helpers_argument_reset
2772   \t_syst_aux{#1}%
2773   \let\m_syst_action_yes\syst_helpers_quintuple_empty_one_yes
2774   \let\m_syst_action_nop\syst_helpers_quintuple_empty_one_nop
2775   \let\if_next_blank_space_token\iffalse
2776   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2777
2778\def\syst_helpers_quintuple_empty_one_yes[#1]%
2779  {\firstargumenttrue
2780   \toksapp\t_syst_aux{[{#1}]}%
2781   \let\m_syst_action_yes\syst_helpers_quintuple_empty_two_yes
2782   \let\m_syst_action_nop\syst_helpers_quintuple_empty_two_nop
2783   \let\if_next_blank_space_token\iffalse
2784   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2785
2786\def\syst_helpers_quintuple_empty_two_yes[#1]%
2787  {\secondargumenttrue
2788   \toksapp\t_syst_aux{[{#1}]}%
2789   \let\m_syst_action_yes\syst_helpers_quintuple_empty_three_yes
2790   \let\m_syst_action_nop\syst_helpers_quintuple_empty_three_nop
2791   \let\if_next_blank_space_token\iffalse
2792   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2793
2794\def\syst_helpers_quintuple_empty_three_yes[#1]%
2795  {\thirdargumenttrue
2796   \toksapp\t_syst_aux{[{#1}]}%
2797   \let\m_syst_action_yes\syst_helpers_quintuple_empty_four_yes
2798   \let\m_syst_action_nop\syst_helpers_quintuple_empty_four_nop
2799   \let\if_next_blank_space_token\iffalse
2800   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2801
2802\def\syst_helpers_quintuple_empty_four_yes[#1]%
2803  {\fourthargumenttrue
2804   \toksapp\t_syst_aux{[{#1}]}%
2805   \let\m_syst_action_yes\syst_helpers_quintuple_empty_five_yes
2806   \let\m_syst_action_nop\syst_helpers_quintuple_empty_five_nop
2807   \let\if_next_blank_space_token\iffalse
2808   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2809
2810\def\syst_helpers_quintuple_empty_one_nop
2811  {\firstargumentfalse
2812   \secondargumentfalse
2813   \thirdargumentfalse
2814   \fourthargumentfalse
2815   \fifthargumentfalse
2816   \the\t_syst_aux[][][][][]}
2817
2818\def\syst_helpers_quintuple_empty_two_nop
2819  {\secondargumentfalse
2820   \thirdargumentfalse
2821   \fourthargumentfalse
2822   \fifthargumentfalse
2823   \if_next_blank_space_token
2824     \expandafter\syst_helpers_empty_spaced_four
2825   \else
2826     \expandafter\syst_helpers_empty_normal_four
2827   \fi}
2828
2829\def\syst_helpers_quintuple_empty_three_nop
2830  {\thirdargumentfalse
2831   \fourthargumentfalse
2832   \fifthargumentfalse
2833   \if_next_blank_space_token
2834     \expandafter\syst_helpers_empty_spaced_three
2835   \else
2836     \expandafter\syst_helpers_empty_normal_three
2837   \fi}
2838
2839\def\syst_helpers_quintuple_empty_four_nop
2840  {\fourthargumentfalse
2841   \fifthargumentfalse
2842   \if_next_blank_space_token
2843     \expandafter\syst_helpers_empty_spaced_two
2844   \else
2845     \expandafter\syst_helpers_empty_normal_two
2846   \fi}
2847
2848\def\syst_helpers_quintuple_empty_five_nop
2849  {\fifthargumentfalse
2850   \if_next_blank_space_token
2851     \expandafter\syst_helpers_empty_spaced_one
2852   \else
2853     \expandafter\syst_helpers_empty_normal_one
2854   \fi}
2855
2856%D Sixtuple:
2857
2858% \protected\def\dosixtupleempty#1%
2859%   {\syst_helpers_argument_reset
2860%    \doifelsenextoptional
2861%      {\syst_helpers_sixtuple_empty_one_yes#1}
2862%      {\syst_helpers_sixtuple_empty_one_nop#1}}
2863%
2864% \def\syst_helpers_sixtuple_empty_one_yes#1[#2]%
2865%   {\firstargumenttrue
2866%    \doifelsenextoptional
2867%      {\syst_helpers_sixtuple_empty_two_yes#1{#2}}%
2868%      {\syst_helpers_sixtuple_empty_two_nop#1{#2}}}
2869%
2870% \def\syst_helpers_sixtuple_empty_two_yes#1#2[#3]%
2871%   {\secondargumenttrue
2872%    \doifelsenextoptional
2873%      {\syst_helpers_sixtuple_empty_three_yes#1{#2}{#3}}%
2874%      {\syst_helpers_sixtuple_empty_three_nop#1{#2}{#3}}}
2875%
2876% \def\syst_helpers_sixtuple_empty_three_yes#1#2#3[#4]%
2877%   {\thirdargumenttrue
2878%    \doifelsenextoptional
2879%      {\syst_helpers_sixtuple_empty_four_yes#1{#2}{#3}{#4}}%
2880%      {\syst_helpers_sixtuple_empty_four_nop#1{#2}{#3}{#4}}}
2881%
2882% \def\syst_helpers_sixtuple_empty_four_yes#1#2#3#4[#5]%
2883%   {\fourthargumenttrue
2884%    \doifelsenextoptional
2885%      {\syst_helpers_sixtuple_empty_five_yes#1{#2}{#3}{#4}{#5}}%
2886%      {\syst_helpers_sixtuple_empty_five_nop#1{#2}{#3}{#4}{#5}}}
2887%
2888% \def\syst_helpers_sixtuple_empty_five_yes#1#2#3#4#5[#6]%
2889%   {\fifthargumenttrue
2890%    \doifelsenextoptional
2891%      {\sixthargumenttrue#1[{#2}][{#3}][{#4}][{#5}][{#6}]}%
2892%      {\syst_helpers_sixtuple_empty_six_nop#1{#2}{#3}{#4}{#5}{#6}}}
2893%
2894% \def\syst_helpers_sixtuple_empty_one_nop#1%
2895%   {\firstargumentfalse
2896%    \secondargumentfalse
2897%    \thirdargumentfalse
2898%    \fourthargumentfalse
2899%    \fifthargumentfalse
2900%    \sixthargumentfalse
2901%    #1[][][][][][]}
2902%
2903% \def\syst_helpers_sixtuple_empty_two_nop
2904%   {\secondargumentfalse
2905%    \thirdargumentfalse
2906%    \fourthargumentfalse
2907%    \fifthargumentfalse
2908%    \sixthargumentfalse
2909%    \if_next_blank_space_token
2910%      \expandafter\syst_helpers_sixtuple_empty_two_spaced
2911%    \else
2912%      \expandafter\syst_helpers_sixtuple_empty_two_normal
2913%    \fi}
2914%
2915% \def\syst_helpers_sixtuple_empty_three_nop
2916%   {\thirdargumentfalse
2917%    \fourthargumentfalse
2918%    \fifthargumentfalse
2919%    \sixthargumentfalse
2920%    \if_next_blank_space_token
2921%      \expandafter\syst_helpers_sixtuple_empty_three_spaced
2922%    \else
2923%      \expandafter\syst_helpers_sixtuple_empty_three_normal
2924%    \fi}
2925%
2926% \def\syst_helpers_sixtuple_empty_four_nop
2927%   {\fourthargumentfalse
2928%    \fifthargumentfalse
2929%    \sixthargumentfalse
2930%    \if_next_blank_space_token
2931%      \expandafter\syst_helpers_sixtuple_empty_four_spaced
2932%    \else
2933%      \expandafter\syst_helpers_sixtuple_empty_four_normal
2934%    \fi}
2935%
2936% \def\syst_helpers_sixtuple_empty_five_nop
2937%   {\fifthargumentfalse
2938%    \sixthargumentfalse
2939%    \if_next_blank_space_token
2940%      \expandafter\syst_helpers_sixtuple_empty_five_spaced
2941%    \else
2942%      \expandafter\syst_helpers_sixtuple_empty_five_normal
2943%    \fi}
2944%
2945% \def\syst_helpers_sixtuple_empty_six_nop
2946%   {\sixthargumentfalse
2947%    \if_next_blank_space_token
2948%      \expandafter\syst_helpers_sixtuple_empty_six_spaced
2949%    \else
2950%      \expandafter\syst_helpers_sixtuple_empty_six_normal
2951%    \fi}
2952%
2953% \def\syst_helpers_sixtuple_empty_two_spaced          #1#2{#1[{#2}][][][][][] }
2954% \def\syst_helpers_sixtuple_empty_two_normal          #1#2{#1[{#2}][][][][][]}
2955% \def\syst_helpers_sixtuple_empty_three_spaced      #1#2#3{#1[{#2}][{#3}][][][][] }
2956% \def\syst_helpers_sixtuple_empty_three_normal      #1#2#3{#1[{#2}][{#3}][][][][]}
2957% \def\syst_helpers_sixtuple_empty_four_spaced     #1#2#3#4{#1[{#2}][{#3}][{#4}][][][] }
2958% \def\syst_helpers_sixtuple_empty_four_normal     #1#2#3#4{#1[{#2}][{#3}][{#4}][][][]}
2959% \def\syst_helpers_sixtuple_empty_five_spaced   #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][][] }
2960% \def\syst_helpers_sixtuple_empty_five_normal   #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][][]}
2961% \def\syst_helpers_sixtuple_empty_six_spaced  #1#2#3#4#5#6{#1[{#2}][{#3}][{#4}][{#5}][{#6}][] }
2962% \def\syst_helpers_sixtuple_empty_six_normal  #1#2#3#4#5#6{#1[{#2}][{#3}][{#4}][{#5}][{#6}][]}
2963
2964\protected\def\dosixtupleempty#1%
2965  {%syst_helpers_argument_reset
2966   \t_syst_aux{#1}%
2967   \let\m_syst_action_yes\syst_helpers_sixtuple_empty_one_yes
2968   \let\m_syst_action_nop\syst_helpers_sixtuple_empty_one_nop
2969   \let\if_next_blank_space_token\iffalse
2970   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2971
2972\def\syst_helpers_sixtuple_empty_one_yes[#1]%
2973  {\firstargumenttrue
2974   \toksapp\t_syst_aux{[{#1}]}%
2975   \let\m_syst_action_yes\syst_helpers_sixtuple_empty_two_yes
2976   \let\m_syst_action_nop\syst_helpers_sixtuple_empty_two_nop
2977   \let\if_next_blank_space_token\iffalse
2978   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2979
2980\def\syst_helpers_sixtuple_empty_two_yes[#1]%
2981  {\secondargumenttrue
2982   \toksapp\t_syst_aux{[{#1}]}%
2983   \let\m_syst_action_yes\syst_helpers_sixtuple_empty_three_yes
2984   \let\m_syst_action_nop\syst_helpers_sixtuple_empty_three_nop
2985   \let\if_next_blank_space_token\iffalse
2986   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2987
2988\def\syst_helpers_sixtuple_empty_three_yes[#1]%
2989  {\thirdargumenttrue
2990   \toksapp\t_syst_aux{[{#1}]}%
2991   \let\m_syst_action_yes\syst_helpers_sixtuple_empty_four_yes
2992   \let\m_syst_action_nop\syst_helpers_sixtuple_empty_four_nop
2993   \let\if_next_blank_space_token\iffalse
2994   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2995
2996\def\syst_helpers_sixtuple_empty_four_yes[#1]%
2997  {\fourthargumenttrue
2998   \toksapp\t_syst_aux{[{#1}]}%
2999   \let\m_syst_action_yes\syst_helpers_sixtuple_empty_five_yes
3000   \let\m_syst_action_nop\syst_helpers_sixtuple_empty_five_nop
3001   \let\if_next_blank_space_token\iffalse
3002   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
3003
3004\def\syst_helpers_sixtuple_empty_five_yes[#1]%
3005  {\fifthargumenttrue
3006   \toksapp\t_syst_aux{[{#1}]}%
3007   \let\m_syst_action_yes\syst_helpers_sixtuple_empty_six_yes
3008   \let\m_syst_action_nop\syst_helpers_sixtuple_empty_six_nop
3009   \let\if_next_blank_space_token\iffalse
3010   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
3011
3012\def\syst_helpers_sixtuple_empty_one_nop
3013  {\firstargumentfalse
3014   \secondargumentfalse
3015   \thirdargumentfalse
3016   \fourthargumentfalse
3017   \fifthargumentfalse
3018   \sixthargumentfalse
3019   \the\t_syst_aux[][][][][][]}
3020
3021\def\syst_helpers_sixtuple_empty_two_nop
3022  {\secondargumentfalse
3023   \thirdargumentfalse
3024   \fourthargumentfalse
3025   \fifthargumentfalse
3026   \sixthargumentfalse
3027   \if_next_blank_space_token
3028     \expandafter\syst_helpers_empty_spaced_five
3029   \else
3030     \expandafter\syst_helpers_empty_normal_five
3031   \fi}
3032
3033\def\syst_helpers_sixtuple_empty_three_nop
3034  {\thirdargumentfalse
3035   \fourthargumentfalse
3036   \fifthargumentfalse
3037   \sixthargumentfalse
3038   \if_next_blank_space_token
3039     \expandafter\syst_helpers_empty_spaced_four
3040   \else
3041     \expandafter\syst_helpers_empty_normal_four
3042   \fi}
3043
3044\def\syst_helpers_sixtuple_empty_four_nop
3045  {\fourthargumentfalse
3046   \fifthargumentfalse
3047   \sixthargumentfalse
3048   \if_next_blank_space_token
3049     \expandafter\syst_helpers_empty_spaced_three
3050   \else
3051     \expandafter\syst_helpers_empty_normal_three
3052   \fi}
3053
3054\def\syst_helpers_sixtuple_empty_five_nop
3055  {\fifthargumentfalse
3056   \sixthargumentfalse
3057   \if_next_blank_space_token
3058     \expandafter\syst_helpers_empty_spaced_two
3059   \else
3060     \expandafter\syst_helpers_empty_normal_two
3061   \fi}
3062
3063\def\syst_helpers_sixtuple_empty_six_nop
3064  {\sixthargumentfalse
3065   \if_next_blank_space_token
3066     \expandafter\syst_helpers_empty_spaced_one
3067   \else
3068     \expandafter\syst_helpers_empty_normal_one
3069   \fi}
3070
3071%D Seventuple:
3072
3073% \protected\def\doseventupleempty#1%
3074%   {\syst_helpers_argument_reset
3075%    \doifelsenextoptional
3076%      {\syst_helpers_seventuple_empty_one_yes#1}%
3077%      {\syst_helpers_seventuple_empty_one_nop#1}}
3078%
3079% \def\syst_helpers_seventuple_empty_one_yes#1[#2]%
3080%   {\firstargumenttrue
3081%    \doifelsenextoptional
3082%      {\syst_helpers_seventuple_empty_two_yes#1{#2}}%
3083%      {\syst_helpers_seventuple_empty_two_nop#1{#2}}}
3084%
3085% \def\syst_helpers_seventuple_empty_two_yes#1#2[#3]%
3086%   {\secondargumenttrue
3087%    \doifelsenextoptional
3088%      {\syst_helpers_seventuple_empty_three_yes#1{#2}{#3}}%
3089%      {\syst_helpers_seventuple_empty_three_nop#1{#2}{#3}}}
3090%
3091% \def\syst_helpers_seventuple_empty_three_yes#1#2#3[#4]%
3092%   {\thirdargumenttrue
3093%    \doifelsenextoptional
3094%      {\syst_helpers_seventuple_empty_four_yes#1{#2}{#3}{#4}}%
3095%      {\syst_helpers_seventuple_empty_four_nop#1{#2}{#3}{#4}}}
3096%
3097% \def\syst_helpers_seventuple_empty_four_yes#1#2#3#4[#5]%
3098%   {\fourthargumenttrue
3099%    \doifelsenextoptional
3100%      {\syst_helpers_seventuple_empty_five_yes#1{#2}{#3}{#4}{#5}}%
3101%      {\syst_helpers_seventuple_empty_five_nop#1{#2}{#3}{#4}{#5}}}
3102%
3103% \def\syst_helpers_seventuple_empty_five_yes#1#2#3#4#5[#6]%
3104%   {\fifthargumenttrue
3105%    \doifelsenextoptional
3106%      {\syst_helpers_seventuple_empty_six_yes#1{#2}{#3}{#4}{#5}{#6}}%
3107%      {\syst_helpers_seventuple_empty_six_nop#1{#2}{#3}{#4}{#5}{#6}}}
3108%
3109% \def\syst_helpers_seventuple_empty_six_yes#1#2#3#4#5#6[#7]%
3110%   {\sixthargumenttrue
3111%    \doifelsenextoptional
3112%      {\seventhargumenttrue#1[{#2}][{#3}][{#4}][{#5}][{#6}][{#7}]}%
3113%      {\syst_helpers_seventuple_empty_seven_nop#1{#2}{#3}{#4}{#5}{#6}{#7}}}
3114%
3115% \def\syst_helpers_seventuple_empty_one_nop#1%
3116%   {\firstargumentfalse
3117%    \secondargumentfalse
3118%    \thirdargumentfalse
3119%    \fourthargumentfalse
3120%    \fifthargumentfalse
3121%    \sixthargumentfalse
3122%    \seventhargumentfalse
3123%    #1[][][][][][][]}
3124%
3125% \def\syst_helpers_seventuple_empty_two_nop
3126%   {\secondargumentfalse
3127%    \thirdargumentfalse
3128%    \fourthargumentfalse
3129%    \fifthargumentfalse
3130%    \sixthargumentfalse
3131%    \seventhargumentfalse
3132%    \if_next_blank_space_token
3133%      \expandafter\syst_helpers_seventuple_empty_two_spaced
3134%    \else
3135%      \expandafter\syst_helpers_seventuple_empty_two_normal
3136%    \fi}
3137%
3138% \def\syst_helpers_seventuple_empty_three_nop
3139%   {\thirdargumentfalse
3140%    \fourthargumentfalse
3141%    \fifthargumentfalse
3142%    \sixthargumentfalse
3143%    \seventhargumentfalse
3144%    \if_next_blank_space_token
3145%      \expandafter\syst_helpers_seventuple_empty_three_spaced
3146%    \else
3147%      \expandafter\syst_helpers_seventuple_empty_three_normal
3148%    \fi}
3149%
3150% \def\syst_helpers_seventuple_empty_four_nop
3151%   {\fourthargumentfalse
3152%    \fifthargumentfalse
3153%    \sixthargumentfalse
3154%    \seventhargumentfalse
3155%    \if_next_blank_space_token
3156%      \expandafter\syst_helpers_seventuple_empty_four_spaced
3157%    \else
3158%      \expandafter\syst_helpers_seventuple_empty_four_normal
3159%    \fi}
3160%
3161% \def\syst_helpers_seventuple_empty_five_nop
3162%   {\fifthargumentfalse
3163%    \sixthargumentfalse
3164%    \seventhargumentfalse
3165%    \if_next_blank_space_token
3166%      \expandafter\syst_helpers_seventuple_empty_five_spaced
3167%    \else
3168%      \expandafter\syst_helpers_seventuple_empty_five_normal
3169%    \fi}
3170%
3171% \def\syst_helpers_seventuple_empty_six_nop
3172%   {\sixthargumentfalse
3173%    \seventhargumentfalse
3174%    \if_next_blank_space_token
3175%      \expandafter\syst_helpers_seventuple_empty_six_spaced
3176%    \else
3177%      \expandafter\syst_helpers_seventuple_empty_six_normal
3178%    \fi}
3179%
3180% \def\syst_helpers_seventuple_empty_seven_nop
3181%   {\seventhargumentfalse
3182%    \if_next_blank_space_token
3183%      \expandafter\syst_helpers_seventuple_empty_seven_spaced
3184%    \else
3185%      \expandafter\syst_helpers_seventuple_empty_seven_normal
3186%    \fi}
3187%
3188% \def\syst_helpers_seventuple_empty_two_spaced            #1#2{#1[{#2}][][][][][][] }
3189% \def\syst_helpers_seventuple_empty_two_normal            #1#2{#1[{#2}][][][][][][]}
3190% \def\syst_helpers_seventuple_empty_three_spaced        #1#2#3{#1[{#2}][{#3}][][][][][] }
3191% \def\syst_helpers_seventuple_empty_three_normal        #1#2#3{#1[{#2}][{#3}][][][][][]}
3192% \def\syst_helpers_seventuple_empty_four_spaced       #1#2#3#4{#1[{#2}][{#3}][{#4}][][][][] }
3193% \def\syst_helpers_seventuple_empty_four_normal       #1#2#3#4{#1[{#2}][{#3}][{#4}][][][][]}
3194% \def\syst_helpers_seventuple_empty_five_spaced     #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][][][] }
3195% \def\syst_helpers_seventuple_empty_five_normal     #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][][][]}
3196% \def\syst_helpers_seventuple_empty_six_spaced    #1#2#3#4#5#6{#1[{#2}][{#3}][{#4}][{#5}][{#6}][][] }
3197% \def\syst_helpers_seventuple_empty_six_normal    #1#2#3#4#5#6{#1[{#2}][{#3}][{#4}][{#5}][{#6}][][]}
3198% \def\syst_helpers_seventuple_empty_seven_spaced#1#2#3#4#5#6#7{#1[{#2}][{#3}][{#4}][{#5}][{#6}][{#7}][] }
3199% \def\syst_helpers_seventuple_empty_seven_normal#1#2#3#4#5#6#7{#1[{#2}][{#3}][{#4}][{#5}][{#6}][{#7}][]}
3200
3201\protected\def\doseventupleempty#1%
3202  {%syst_helpers_argument_reset
3203   \t_syst_aux{#1}%
3204   \let\m_syst_action_yes\syst_helpers_seventuple_empty_one_yes
3205   \let\m_syst_action_nop\syst_helpers_seventuple_empty_one_nop
3206   \let\if_next_blank_space_token\iffalse
3207   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
3208
3209\def\syst_helpers_seventuple_empty_one_yes[#1]%
3210  {\firstargumenttrue
3211   \toksapp\t_syst_aux{[{#1}]}%
3212   \let\m_syst_action_yes\syst_helpers_seventuple_empty_two_yes
3213   \let\m_syst_action_nop\syst_helpers_seventuple_empty_two_nop
3214   \let\if_next_blank_space_token\iffalse
3215   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
3216
3217\def\syst_helpers_seventuple_empty_two_yes[#1]%
3218  {\secondargumenttrue
3219   \toksapp\t_syst_aux{[{#1}]}%
3220   \let\m_syst_action_yes\syst_helpers_seventuple_empty_three_yes
3221   \let\m_syst_action_nop\syst_helpers_seventuple_empty_three_nop
3222   \let\if_next_blank_space_token\iffalse
3223   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
3224
3225\def\syst_helpers_seventuple_empty_three_yes[#1]%
3226  {\thirdargumenttrue
3227   \toksapp\t_syst_aux{[{#1}]}%
3228   \let\m_syst_action_yes\syst_helpers_seventuple_empty_four_yes
3229   \let\m_syst_action_nop\syst_helpers_seventuple_empty_four_nop
3230   \let\if_next_blank_space_token\iffalse
3231   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
3232
3233\def\syst_helpers_seventuple_empty_four_yes[#1]%
3234  {\fourthargumenttrue
3235   \toksapp\t_syst_aux{[{#1}]}%
3236   \let\m_syst_action_yes\syst_helpers_seventuple_empty_five_yes
3237   \let\m_syst_action_nop\syst_helpers_seventuple_empty_five_nop
3238   \let\if_next_blank_space_token\iffalse
3239   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
3240
3241\def\syst_helpers_seventuple_empty_five_yes[#1]%
3242  {\fifthargumenttrue
3243   \toksapp\t_syst_aux{[{#1}]}%
3244   \let\m_syst_action_yes\syst_helpers_seventuple_empty_six_yes
3245   \let\m_syst_action_nop\syst_helpers_seventuple_empty_six_nop
3246   \let\if_next_blank_space_token\iffalse
3247   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
3248
3249\def\syst_helpers_seventuple_empty_six_yes[#1]%
3250  {\sixthargumenttrue
3251   \toksapp\t_syst_aux{[{#1}]}%
3252   \let\m_syst_action_yes\syst_helpers_seventuple_empty_seven_yes
3253   \let\m_syst_action_nop\syst_helpers_seventuple_empty_seven_nop
3254   \let\if_next_blank_space_token\iffalse
3255   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
3256
3257\def\syst_helpers_seventuple_empty_one_nop
3258  {\firstargumentfalse
3259   \secondargumentfalse
3260   \thirdargumentfalse
3261   \fourthargumentfalse
3262   \fifthargumentfalse
3263   \sixthargumentfalse
3264   \seventhargumentfalse
3265   \the\t_syst_aux[][][][][][][]}
3266
3267\def\syst_helpers_seventuple_empty_two_nop
3268  {\secondargumentfalse
3269   \thirdargumentfalse
3270   \fourthargumentfalse
3271   \fifthargumentfalse
3272   \sixthargumentfalse
3273   \seventhargumentfalse
3274   \if_next_blank_space_token
3275     \expandafter\syst_helpers_empty_spaced_six
3276   \else
3277     \expandafter\syst_helpers_empty_normal_six
3278   \fi}
3279
3280\def\syst_helpers_seventuple_empty_three_nop
3281  {\thirdargumentfalse
3282   \fourthargumentfalse
3283   \fifthargumentfalse
3284   \sixthargumentfalse
3285   \seventhargumentfalse
3286   \if_next_blank_space_token
3287     \expandafter\syst_helpers_empty_spaced_five
3288   \else
3289     \expandafter\syst_helpers_empty_normal_five
3290   \fi}
3291
3292\def\syst_helpers_seventuple_empty_four_nop
3293  {\fourthargumentfalse
3294   \fifthargumentfalse
3295   \sixthargumentfalse
3296   \seventhargumentfalse
3297   \if_next_blank_space_token
3298     \expandafter\syst_helpers_empty_spaced_four
3299   \else
3300     \expandafter\syst_helpers_empty_normal_four
3301   \fi}
3302
3303\def\syst_helpers_seventuple_empty_five_nop
3304  {\fifthargumentfalse
3305   \sixthargumentfalse
3306   \seventhargumentfalse
3307   \if_next_blank_space_token
3308     \expandafter\syst_helpers_empty_spaced_three
3309   \else
3310     \expandafter\syst_helpers_empty_normal_three
3311   \fi}
3312
3313\def\syst_helpers_seventuple_empty_six_nop
3314  {\sixthargumentfalse
3315   \seventhargumentfalse
3316   \if_next_blank_space_token
3317     \expandafter\syst_helpers_empty_spaced_two
3318   \else
3319     \expandafter\syst_helpers_empty_normal_two
3320   \fi}
3321
3322\def\syst_helpers_seventuple_empty_seven_nop
3323  {\seventhargumentfalse
3324   \if_next_blank_space_token
3325     \expandafter\syst_helpers_empty_spaced_one
3326   \else
3327     \expandafter\syst_helpers_empty_normal_one
3328   \fi}
3329
3330%D Aliases:
3331
3332\let\dosingleargument    \dosingleempty
3333\let\dodoubleargument    \dodoubleempty
3334\let\dotripleargument    \dotripleempty
3335\let\doquadrupleargument \doquadrupleempty
3336\let\doquintupleargument \doquintupleempty
3337\let\dosixtupleargument  \dosixtupleempty
3338\let\doseventupleargument\doseventupleempty
3339
3340%D \macros
3341%D   {strippedcsname}
3342%D
3343%D The next macro can be very useful when using \type{\csname} like in:
3344%D
3345%D \starttyping
3346%D \csname if\strippedcsname\something\endcsname
3347%D \stoptyping
3348%D
3349%D This expands to \type{\ifsomething}.
3350
3351% \def\strippedcsname
3352%   {\expandafter\gobbleoneargument\string}
3353
3354\let\strippedcsname\csstring
3355
3356%D \macros
3357%D   {complexorsimple,complexorsimpleempty}
3358%D
3359%D Setups can be optional. A command expecting a setup is prefixed by \type
3360%D {\complex}, a command without one gets the prefix \type {\simple}. Commands like
3361%D this can be defined by:
3362%D
3363%D \starttyping
3364%D \complexorsimple\command
3365%D \stoptyping
3366%D
3367%D When \type{\command} is followed by a \type{[setup]}, then
3368%D
3369%D \starttyping
3370%D \complexcommand [setup]
3371%D \stoptyping
3372%D
3373%D executes, else we get
3374%D
3375%D \starttyping
3376%D \simplecommand
3377%D \stoptyping
3378%D
3379%D An alternative for \type{\complexorsimple} is:
3380%D
3381%D \starttyping
3382%D \complexorsimpleempty {command}
3383%D \stoptyping
3384%D
3385%D Depending on the presence of \type{[setup]}, this one leads to one of:
3386%D
3387%D \starttyping
3388%D \complexcommando [setup]
3389%D \complexcommando []
3390%D \stoptyping
3391%D
3392%D Many \CONTEXT\ commands started as complex or simple ones, but changed into more
3393%D versatile (more object oriented) ones using the \type {\get..argument} commands.
3394
3395\protected\def\complexorsimple#1%
3396  {% \relax % prevents lookahead, brrr
3397   \doifelsenextoptional
3398     {\firstargumenttrue \csname\s!complex\csstring#1\endcsname}
3399     {\firstargumentfalse\csname\s!simple \csstring#1\endcsname}}
3400
3401\protected\def\complexorsimpleempty#1%
3402  {% \relax % prevents lookahead, brrr
3403   \doifelsenextoptional
3404     {\firstargumenttrue \csname\s!complex\csstring#1\endcsname}
3405     {\firstargumentfalse\csname\s!complex\csstring#1\endcsname[]}}
3406
3407%D \macros
3408%D   {definecomplexorsimple,definecomplexorsimpleempty}
3409%D
3410%D The previous commands are used that often that we found it worthwile to offer two
3411%D more alternatives. Watch the build in protection.
3412
3413\protected\def\syst_helpers_complex_or_simple#1#2%
3414  {\doifelsenextoptional{\firstargumenttrue#1}{\firstargumentfalse#2}}
3415
3416\protected\def\syst_helpers_complex_or_simple_empty#1%
3417  {\doifelsenextoptional{\firstargumenttrue#1}{\firstargumentfalse#1[]}}
3418
3419\protected\def\definecomplexorsimple#1%
3420  {\protected\edef#1{\syst_helpers_complex_or_simple
3421     \expandafter\noexpand\csname\s!complex\csstring#1\endcsname
3422     \expandafter\noexpand\csname\s!simple \csstring#1\endcsname}}
3423
3424\protected\def\definecomplexorsimpleempty#1%
3425  {\protected\edef#1{\syst_helpers_complex_or_simple_empty
3426     \expandafter\noexpand\csname\s!complex\csstring#1\endcsname}}
3427
3428%D These commands are called as:
3429%D
3430%D \starttyping
3431%D \definecomplexorsimple\command
3432%D \stoptyping
3433%D
3434%D Of course, we must have available
3435%D
3436%D \starttyping
3437%D \def\complexcommand[#1]{...}
3438%D \def\simplecommand     {...}
3439%D \stoptyping
3440%D
3441%D Using this construction saves a few string now and then.
3442
3443%D \macros
3444%D   {dosinglegroupempty,dodoublegroupempty,dotriplegroupempty,
3445%D    doquadruplegroupempty, doquintuplegroupempty}
3446%D
3447%D We've already seen some commands that take care of
3448%D optional arguments between \type{[]}. The next two commands
3449%D handle the ones with \type{{}}. They are called as:
3450%D
3451%D \starttyping
3452%D \dosinglegroupempty    \ineedONEargument
3453%D \dodoublegroupempty    \ineedTWOarguments
3454%D \dotriplegroupempty    \ineedTHREEarguments
3455%D \doquadruplegroupempty \ineedFOURarguments
3456%D \doquintuplegroupempty \ineedFIVEarguments
3457%D \stoptyping
3458%D
3459%D We can add additional definitions later when we have defined \type {\appendtoks}.
3460
3461\newconditional\c_syst_helpers_permit_spaces_between_groups
3462
3463\protected\def    \permitspacesbetweengroups{\settrue \c_syst_helpers_permit_spaces_between_groups}
3464\protected\def\dontpermitspacesbetweengroups{\setfalse\c_syst_helpers_permit_spaces_between_groups}
3465
3466\dontpermitspacesbetweengroups
3467
3468%D We can avoid the nasty if handling in \type {syst-gen} by splitting the lot in
3469%D pieces so that we have no nested \type {\nextarguments} potentially being an
3470%D \type {conditional} token. Okay, these macros are not called that often but it
3471%D saves crap when tracing.
3472
3473\protected\def\syst_helpers_get_grouped_argument#1#2%
3474  {\let\syst_helpers_get_grouped_argument_yes#1%
3475   \let\syst_helpers_get_grouped_argument_nop#2%
3476   \futurelet\nextargument\syst_helpers_get_grouped_argument_indeed}
3477
3478\def\syst_helpers_get_grouped_argument_indeed
3479  {\ifx\nextargument\bgroup
3480     \expandafter\syst_helpers_get_grouped_argument_a
3481   \else
3482     \expandafter\syst_helpers_get_grouped_argument_b
3483   \fi}
3484
3485\def\syst_helpers_get_grouped_argument_a
3486  {%syst_helpers_argument_reset
3487   \syst_helpers_get_grouped_argument_yes\syst_helpers_get_grouped_argument_nested}
3488
3489\def\syst_helpers_get_grouped_argument_b
3490  {\ifconditional\c_syst_helpers_permit_spaces_between_groups
3491     \expandafter\syst_helpers_get_grouped_argument_f
3492   \else
3493     \expandafter\syst_helpers_get_grouped_argument_d
3494   \fi}
3495
3496\def\syst_helpers_get_grouped_argument_d
3497  {%syst_helpers_argument_error
3498   \syst_helpers_get_grouped_argument_nop\syst_helpers_get_grouped_argument_nested{}}
3499
3500\begingroup
3501  \def\\ {\syst_helpers_get_grouped_argument\syst_helpers_get_grouped_argument_yes\syst_helpers_get_grouped_argument_nop}
3502  \glet\syst_helpers_get_grouped_argument_e\\
3503\endgroup
3504
3505\def\syst_helpers_get_grouped_argument_f
3506  {\ifx\nextargument\blankspace
3507     \expandafter\syst_helpers_get_grouped_argument_e % g
3508   \else
3509     \expandafter\syst_helpers_get_grouped_argument_d % h
3510   \fi}
3511
3512\protected\def\dosinglegroupempty#1%
3513  {\def\syst_helpers_get_grouped_argument_nested
3514     {\dontpermitspacesbetweengroups
3515      #1}%
3516   \syst_helpers_get_grouped_argument\firstargumenttrue\firstargumentfalse}
3517
3518\protected\def\dodoublegroupempty#1%
3519  {\def\syst_helpers_get_grouped_argument_nested##1%
3520     {\def\syst_helpers_get_grouped_argument_nested
3521        {\dontpermitspacesbetweengroups
3522         #1{##1}}%
3523      \syst_helpers_get_grouped_argument\secondargumenttrue\secondargumentfalse}%
3524   \syst_helpers_get_grouped_argument\firstargumenttrue\firstargumentfalse}
3525
3526\protected\def\dotriplegroupempty#1%
3527  {\def\syst_helpers_get_grouped_argument_nested##1%
3528     {\def\syst_helpers_get_grouped_argument_nested####1%
3529        {\def\syst_helpers_get_grouped_argument_nested
3530           {\dontpermitspacesbetweengroups
3531            #1{##1}{####1}}%
3532         \syst_helpers_get_grouped_argument\thirdargumenttrue\thirdargumentfalse}%
3533      \syst_helpers_get_grouped_argument\secondargumenttrue\secondargumentfalse}%
3534   \syst_helpers_get_grouped_argument\firstargumenttrue\firstargumentfalse}
3535
3536\protected\def\doquadruplegroupempty#1%
3537  {\def\syst_helpers_get_grouped_argument_nested##1%
3538     {\def\syst_helpers_get_grouped_argument_nested####1%
3539        {\def\syst_helpers_get_grouped_argument_nested########1%
3540           {\def\syst_helpers_get_grouped_argument_nested
3541              {\dontpermitspacesbetweengroups
3542               #1{##1}{####1}{########1}}%
3543            \syst_helpers_get_grouped_argument\fourthargumenttrue\fourthargumentfalse}%
3544         \syst_helpers_get_grouped_argument\thirdargumenttrue\thirdargumentfalse}%
3545      \syst_helpers_get_grouped_argument\secondargumenttrue\secondargumentfalse}%
3546   \syst_helpers_get_grouped_argument\firstargumenttrue\firstargumentfalse}
3547
3548\protected\def\doquintuplegroupempty#1%
3549  {\def\syst_helpers_get_grouped_argument_nested##1%
3550     {\def\syst_helpers_get_grouped_argument_nested####1%
3551        {\def\syst_helpers_get_grouped_argument_nested########1%
3552           {\def\syst_helpers_get_grouped_argument_nested################1%
3553             {\def\syst_helpers_get_grouped_argument_nested
3554                {\dontpermitspacesbetweengroups
3555                 #1{##1}{####1}{########1}{################1}}%
3556              \syst_helpers_get_grouped_argument\fifthargumenttrue\fifthargumentfalse}%
3557            \syst_helpers_get_grouped_argument\fourthargumenttrue\fourthargumentfalse}%
3558         \syst_helpers_get_grouped_argument\thirdargumenttrue\thirdargumentfalse}%
3559      \syst_helpers_get_grouped_argument\secondargumenttrue\secondargumentfalse}%
3560   \syst_helpers_get_grouped_argument\firstargumenttrue\firstargumentfalse}
3561
3562%D These macros can explictly take care of spaces, which means that the next
3563%D definition and calls are valid:
3564%D
3565%D \starttyping
3566%D \def\test#1#2#3{[#1#2#3]}
3567%D
3568%D \dotriplegroupempty\test {a}{b}{c}
3569%D \dotriplegroupempty\test {a}{b}
3570%D \dotriplegroupempty\test {a}
3571%D \dotriplegroupempty\test
3572%D \dotriplegroupempty\test {a} {b} {c}
3573%D \dotriplegroupempty\test {a} {b}
3574%D \dotriplegroupempty\test
3575%D   {a}
3576%D   {b}
3577%D \stoptyping
3578%D
3579%D And alike.
3580
3581%D \macros
3582%D   {firstofoneargument, firstoftwoarguments, firstofthreearguments
3583%D    secondoftwoarguments, secondofthreearguments,
3584%D    thirdofthreearguments}
3585%D
3586%D The next six macros (dedicated to Taco) can conveniently used to select
3587%D arguments. Their names explain their functionality.
3588
3589\def\firstofoneargument            #1{#1}
3590
3591\def\firstoftwoarguments         #1#2{#1}
3592\def\secondoftwoarguments        #1#2{#2}
3593
3594\def\firstofthreearguments     #1#2#3{#1}
3595\def\secondofthreearguments    #1#2#3{#2}
3596\def\thirdofthreearguments     #1#2#3{#3}
3597
3598\def\firstoffourarguments    #1#2#3#4{#1}
3599\def\secondoffourarguments   #1#2#3#4{#2}
3600\def\thirdoffourarguments    #1#2#3#4{#3}
3601\def\fourthoffourarguments   #1#2#3#4{#4}
3602
3603\def\firstoffivearguments  #1#2#3#4#5{#1}
3604\def\secondoffivearguments #1#2#3#4#5{#2}
3605\def\thirdoffivearguments  #1#2#3#4#5{#3}
3606\def\fourthoffivearguments #1#2#3#4#5{#4}
3607\def\fifthoffivearguments  #1#2#3#4#5{#5}
3608
3609\def\firstofsixarguments #1#2#3#4#5#6{#1}
3610\def\secondofsixarguments#1#2#3#4#5#6{#2}
3611\def\thirdofsixarguments #1#2#3#4#5#6{#3}
3612\def\fourthofsixarguments#1#2#3#4#5#6{#4}
3613\def\fifthofsixarguments #1#2#3#4#5#6{#5}
3614\def\sixthofsixarguments #1#2#3#4#5#6{#6}
3615
3616\protected\def\firstofoneunexpanded       #1{#1}
3617
3618\protected\def\firstoftwounexpanded     #1#2{#1}
3619\protected\def\secondoftwounexpanded    #1#2{#2}
3620
3621\protected\def\firstofthreeunexpanded #1#2#3{#1}
3622\protected\def\secondofthreeunexpanded#1#2#3{#2}
3623\protected\def\thirdofthreeunexpanded #1#2#3{#3}
3624
3625%D \macros
3626%D   {globalletempty,letempty,
3627%D    letvalueempty,letgvalueempty,
3628%D    letvaluerelax,letgvaluerelax}
3629%D
3630%D Trivial:
3631
3632\protected\def\letempty      #1{\let #1\empty}
3633\protected\def\globalletempty#1{\glet#1\empty}
3634
3635\protected\def\letvalueempty #1{\expandafter\let \csname#1\endcsname\empty}
3636\protected\def\letgvalueempty#1{\expandafter\glet\csname#1\endcsname\empty}
3637\protected\def\letvaluerelax #1{\expandafter\let \csname#1\endcsname\relax}
3638\protected\def\letgvalurelax #1{\expandafter\glet\csname#1\endcsname\relax}
3639
3640\protected\def\relaxvalueifundefined#1%
3641  {\ifcsname#1\endcsname \else
3642     \expandafter\let\csname#1\endcsname\relax
3643   \fi}
3644
3645%D \macros
3646%D   {wait}
3647%D
3648%D The next macro hardly needs explanation. Because no nesting is to be expected, we
3649%D can reuse \type {\wait} within \type {\wait} itself.
3650
3651\protected\def\wait
3652  {\begingroup
3653   \read16 to \wait
3654   \endgroup}
3655
3656%D \macros
3657%D   {writestring,writeline,
3658%D    writestatus,statuswidth,normalwritestatus}
3659%D
3660%D Maybe one didn't notice, but we've already introduced a macro for showing
3661%D messages. In the multi||lingual modules, we will also introduce a mechanism for
3662%D message passing. For the moment we stick to the core macros:
3663%D
3664%D \starttyping
3665%D \writestring {string}
3666%D \writeline
3667%D \writestatus {category} {message}
3668%D \stoptyping
3669%D
3670%D Messages are formatted. One can provide the maximum with of the identification
3671%D string with the macro \type {\statuswidth}.
3672
3673\setnewconstant\statuswidth  15
3674\setnewconstant\statuswrite 128 % \pluscxxviii
3675
3676\ifdefined\writestring \else
3677
3678    \protected\def\writestring{\immediate\write\statuswrite}
3679    \protected\def\writeline  {\writestring{}}
3680
3681\fi
3682
3683\protected\def\normalwritestatus#1#2%
3684  {\writestring{\expandafter\syst_helpers_split_status_yes\expandafter\statuswidth#1%
3685     \space\space\space\space\space\space\space
3686     \space\space\space\space\space\space\space
3687     \space\space\space\space\space\space\end
3688     \space:\space#2}}
3689
3690\def\syst_helpers_split_status_yes#1#2%
3691  {\ifcase#1 \expandafter\syst_helpers_split_status_nop\fi#2%
3692   \expandafter\syst_helpers_split_status_yes\expandafter{\the\numexpr#1+\minusone\relax}}
3693
3694\def\syst_helpers_split_status_nop#1\end
3695  {}
3696
3697%D \macros
3698%D   {immediatemessage}
3699%D
3700%D A fully expandable message:
3701
3702\let\immediatemessage\clf_immediatemessage % {} mandate
3703
3704%D \macros
3705%D   {rawgetparameters}
3706%D
3707%D A raw and dirty alternative for \type {\getparameters}; no checking is done!
3708
3709\protected\def\rawsetparameter#1=#2,%
3710  {\if]#1\else
3711     \expandafter\def\csname\rawparameterprefix#1\endcsname{#2}%
3712     \expandafter\rawsetparameter
3713   \fi}
3714
3715\protected\def\rawgetparameters[#1][#2% some 5-10% faster
3716  {\ifx#2]% test is needed, else bomb on [#1][]
3717     \expandafter\gobbleoneargument
3718   \else
3719     \def\rawparameterprefix{#1}%
3720     \expandafter\dorawgetparameters
3721   \fi#2}
3722
3723\def\dorawgetparameters#1]%
3724  {\expandafter\rawsetparameter#1,]=,}
3725
3726%D \macros
3727%D   {doglobal,
3728%D    redoglobal,dodoglobal,resetglobal}
3729%D
3730%D The two macros \type {\redoglobal} and \type{\dodoglobal} are used in this and
3731%D some other modules to enforce a user specified \type {\doglobal} action. The last
3732%D and often only global assignment in a macro is done with \type {\dodoglobal}, but
3733%D all preceding ones with \type {\redoglobal}. When using only alternatives, one
3734%D can reset this mechanism with \type {\resetglobal}.
3735
3736\protected\def\resetglobal
3737  {\let\redoglobal\relax
3738   \let\dodoglobal\relax}
3739
3740\resetglobal
3741
3742\protected\def\doglobal
3743  {\ifx\redoglobal\relax
3744     \let\redoglobal\global
3745     \let\dodoglobal\syst_helpers_dodo_global
3746   \fi}
3747
3748\def\syst_helpers_dodo_global
3749  {\resetglobal\global}
3750
3751\def\saveglobal
3752  {\let\syst_helpers_dodo_global\dodoglobal
3753   \let\syst_helpers_redo_global\redoglobal}
3754
3755\def\restoreglobal
3756  {\let\redoglobal\syst_helpers_redo_global
3757   \let\dodoglobal\syst_helpers_dodo_global}
3758
3759%D A very useful application of this macro is \type {\newif}, \TEX's fake boolean
3760%D type. Not being a primitive, \type {\global} hopelessly fails here. But a slight
3761%D adaption of Knuth's original macro permits:
3762%D
3763%D \starttyping
3764%D \doglobal\newif\iftest
3765%D \stoptyping
3766%D
3767%D Of course one can still say:
3768%D
3769%D \starttyping
3770%D \global\testtrue
3771%D \global\testfalse
3772%D \stoptyping
3773%D
3774%D Apart from the prefixes, a few more \type {\expandafters} are needed:
3775
3776% \protected\def\newif#1% uses the original plain \@if
3777%   {\privatescratchcounter\escapechar
3778%    \escapechar\minusone
3779%    \expandafter\expandafter\expandafter
3780%      \redoglobal\expandafter\expandafter\expandafter
3781%        \edef\@if#1{true}{\let\noexpand#1\noexpand\iftrue}%
3782%    \expandafter\expandafter\expandafter
3783%      \redoglobal\expandafter\expandafter\expandafter
3784%        \edef\@if#1{false}{\let\noexpand#1\noexpand\iffalse}%
3785%    \dodoglobal\@if#1{false}%
3786%    \escapechar\privatescratchcounter}
3787
3788\protected\def\newif#1% see syst-ini.mkiv
3789  {\let\new_if_saved\newif
3790   \let\newif\new_if_check
3791   \expandafter\redoglobal\expandafter\def\csname\expandafter\newif\csstring#1true\endcsname {\let#1\iftrue }%
3792   \expandafter\redoglobal\expandafter\def\csname\expandafter\newif\csstring#1false\endcsname{\let#1\iffalse}%
3793   \dodoglobal\csname\expandafter\newif\csstring#1false\endcsname
3794   \let\newif\new_if_saved}
3795
3796%D Also new:
3797
3798\protected\def\define#1%
3799  {\ifdefined#1%
3800     \message{[\noexpand#1is already defined]}%
3801     \protected\expandafter\def\expandafter\gobbleddefinition
3802   \else
3803     \protected\expandafter\def
3804   \fi#1}
3805
3806\protected\def\redefine#1%
3807  {\ifdefined#1%
3808     \message{[\noexpand#1is redefined]}%
3809   \fi
3810   \protected\def#1}
3811
3812\protected\def\definemacro#1%
3813  {\ifdefined#1%
3814     \message{[\noexpand#1is already defined]}%
3815     \protected\expandafter\def\expandafter\gobbleddefinition
3816   \else
3817     \protected\expandafter\def
3818   \fi#1}
3819
3820% \define\hans{hans}
3821% \redefine\hans{hans}
3822% \define\hans#1[]#2#3{hans}
3823
3824%D The next variant fits nicely in the setups syntax:
3825%D
3826%D \starttyping
3827%D \starttexdefinition bagger [#1] #2
3828%D     oeps
3829%D     #1
3830%D     oeps
3831%D \stoptexdefinition
3832%D
3833%D \bagger [a] {b}
3834%D \stoptyping
3835
3836% \starttexdefinition test
3837% oeps
3838% \stoptexdefinition
3839%
3840% [\test]
3841
3842\def\s!unexpanded{unexpanded}
3843
3844% \bgroup \obeylines
3845%
3846% \glet\stoptexdefinition\relax
3847%
3848% \protected\gdef\starttexdefinition%
3849%   {\bgroup%
3850%    \obeylines%
3851%    \syst_helpers_start_tex_definition}
3852%
3853% \gdef\syst_helpers_start_tex_definition #1
3854%   {\catcode\endoflineasciicode\ignorecatcode%
3855%    \doifinstringelse\letterhash{\detokenize{#1}}\syst_helpers_start_tex_definition_yes\syst_helpers_start_tex_definition_nop#1
3856%   }
3857%
3858% \gdef\syst_helpers_start_tex_definition_yes#1 #2
3859%   {\edef\texdefinitionname{#1}%
3860%    \ifx\texdefinitionname\s!unexpanded%
3861%      \expandafter\syst_helpers_start_tex_definition_yes_unexpanded%
3862%    \else%
3863%      \expandafter\syst_helpers_start_tex_definition_yes_normal%
3864%    \fi%
3865%    {#1}#2
3866%    }
3867%
3868% \gdef\syst_helpers_start_tex_definition_yes_unexpanded#1#2 #3
3869%                                        #4\stoptexdefinition%
3870%   {\egroup% #1=unexpanded
3871%    \protected\expandafter\def\csname#2\endcsname#3{#4}}
3872%
3873% \gdef\syst_helpers_start_tex_definition_yes_normal#1#2
3874%                                     #3\stoptexdefinition%
3875%   {\egroup%
3876%    \expandafter\def\csname#1\endcsname#2{#3}}
3877%
3878% \gdef\syst_helpers_start_tex_definition_nop#1
3879%   {\syst_helpers_start_tex_definition_nop_indeed{#1}{}}
3880%
3881% \gdef\syst_helpers_start_tex_definition_nop_indeed#1#2#3\stoptexdefinition%
3882%   {\egroup%
3883%    \expandafter\def\csname#1\endcsname{#3}}
3884%
3885% \egroup
3886
3887% \starttexdefinition unexpanded test #1
3888%     [here #1]
3889% \stoptexdefinition
3890%
3891% \starttexdefinition global unexpanded test
3892%     [here test]
3893% \stoptexdefinition
3894%
3895% \scratchcounter=123
3896%
3897% \starttexdefinition global unexpanded expanded test #oeps
3898%     [here #oeps: \the\scratchcounter]
3899% \stoptexdefinition
3900
3901% \bgroup \obeylines
3902%
3903% \glet\stoptexdefinition\relax
3904%
3905% \protected\gdef\starttexdefinition%
3906%   {\bgroup%
3907%    \obeylines%
3908%    \syst_helpers_start_tex_definition_one}
3909%
3910% \gdef\syst_helpers_start_tex_definition_one#1
3911%   {\catcode\endoflineasciicode\ignorecatcode%
3912%    \syst_helpers_start_tex_definition_two{#1}}
3913%
3914% \gdef\syst_helpers_start_tex_definition_two#1#2\stoptexdefinition%
3915%   {\egroup%
3916%    \ctxcommand{thetexdefinition("#1")}{#2}}
3917%
3918% \egroup
3919
3920\bgroup \obeylines
3921
3922\glet\stoptexdefinition\relax
3923
3924\protected\gdef\starttexdefinition%
3925  {\bgroup%
3926   \obeylines%
3927   \syst_helpers_start_tex_definition}
3928
3929\gdef\syst_helpers_start_tex_definition#1
3930  {\catcode\endoflineasciicode\ignorecatcode%
3931   \clf_texdefinition_one{#1}}
3932
3933\gdef\dostarttexdefinition#1\stoptexdefinition%
3934  {\egroup%
3935   \clf_texdefinition_two{#1}}
3936
3937\egroup
3938
3939% \protected\def\texdefinition#1{\csname\ifcsname#1\endcsname#1\else donothing\fi\endcsname} % todo: a nop cs: char 0 or some corenamespace
3940
3941\protected\def\texdefinition#1{\begincsname#1\endcsname}
3942
3943% This is a first variant, more might be added:
3944
3945\protected\def\starttexcode{\unprotect}
3946\protected\def\stoptexcode {\protect}
3947
3948%D \macros
3949%D   {newcounter,
3950%D    increment,decrement}
3951%D
3952%D Unfortunately the number of \COUNTERS\ in \TEX\ is limited, but fortunately we
3953%D can store numbers in a macro. We can increment such pseudo \COUNTERS\ with \type
3954%D {\increment}.
3955%D
3956%D \starttyping
3957%D \increment(\counter,20)
3958%D \increment(\counter,-4)
3959%D \increment(\counter)
3960%D \increment\counter
3961%D \stoptyping
3962%D
3963%D After this sequence of commands, the value of \type {\counter} is 20, 16, 17
3964%D and~18. Of course there is also the complementary command \type {\decrement}.
3965%D
3966%D Global assignments are possible too, using \type{\doglobal}:
3967%D
3968%D \starttyping
3969%D \doglobal\increment\counter
3970%D \stoptyping
3971%D
3972%D When \type {\counter} is undefined, it's value is initialized at~0. It is
3973%D nevertheless better to define a \COUNTER\ explicitly. One reason could be that
3974%D the \COUNTER\ can be part of a test with \type {\ifnum} and this conditional does
3975%D not accept undefined macro's. The \COUNTER\ in our example can for instance be
3976%D defined with:
3977%D
3978%D \starttyping
3979%D \newcounter\counter
3980%D \stoptyping
3981%D
3982%D The command \type {\newcounter} must not be confused with \type {\newcount}! Of
3983%D course this mechanism is much slower than using \TEX's \COUNTERS\ directly. In
3984%D practice \COUNTERS\ (and therefore our pseudo counters too) are seldom the
3985%D bottleneck in the processing of a text. Apart from some other incompatilities we
3986%D want to mention a pitfal when using \type {\ifnum}.
3987%D
3988%D \starttyping
3989%D \ifnum\normalcounter=\pseudocounter \doif \else \doelse \fi
3990%D \ifnum\pseudocounter=\normalcounter \doif \else \doelse \fi
3991%D \stoptyping
3992%D
3993%D In the first test, \TEX\ continues it's search for the second number after
3994%D reading \type {\pseudocounter}, while in the second test, it stops reading after
3995%D having encountered a real one. Tests like the first one therefore can give
3996%D unexpected results, for instance execution of \type {\doif} even if both numbers
3997%D are unequal.
3998
3999\def\zerocountervalue{0}
4000
4001\protected\def\newcounter#1%
4002  {\dodoglobal\let#1\zerocountervalue}
4003
4004%D Nowadays we don't mind a few more tokens if we can gain a bit of speed.
4005
4006\def\syst_helpers_do_increment#1{\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi+\plusone \relax}}
4007\def\syst_helpers_do_decrement#1{\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi+\minusone\relax}}
4008
4009\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}}
4010\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}}
4011
4012\def\syst_helpers_do_do_increment(#1%
4013  {\def\m_syst_action_yes{\syst_helpers_do_do_do_increment#1}%
4014   \def\m_syst_action_nop{\syst_helpers_do_do_do_increment#1,\plusone}%
4015   \doifelsenextcharcs,\m_syst_action_yes\m_syst_action_nop}
4016
4017\def\syst_helpers_do_do_decrement(#1%
4018  {\def\m_syst_action_yes{\syst_helpers_do_do_do_decrement#1}%
4019   \def\m_syst_action_nop{\syst_helpers_do_do_do_decrement#1,\plusone}%
4020   \doifelsenextcharcs,\m_syst_action_yes\m_syst_action_nop}
4021
4022\protected\def\increment{\doifelsenextcharcs(\syst_helpers_do_do_increment\syst_helpers_do_increment}
4023\protected\def\decrement{\doifelsenextcharcs(\syst_helpers_do_do_decrement\syst_helpers_do_decrement}
4024
4025\protected\def\fastincrement#1{\dodoglobal\edef#1{\the\numexpr#1+\plusone \relax}}
4026\protected\def\fastdecrement#1{\dodoglobal\edef#1{\the\numexpr#1+\minusone\relax}}
4027
4028\protected\def\incrementvalue#1{\expandafter\increment\csname#1\endcsname}
4029\protected\def\decrementvalue#1{\expandafter\decrement\csname#1\endcsname}
4030
4031%D \macros
4032%D  {newsignal}
4033%D
4034%D When writing advanced macros, we cannot do without signaling. A signal is a small
4035%D (invisible) kern or penalty that signals the next macro that something just
4036%D happened. This macro can take any action depending on the previous signal.
4037%D Signals must be unique and the next macro takes care of that.
4038%D
4039%D \starttyping
4040%D \newsignal\somesignal
4041%D \stoptyping
4042%D
4043%D Signals old dimensions and can be used in skips, kerns and tests like \type
4044%D {\ifdim}.
4045
4046\newdimen\maximumsignal % step is about 0.00025pt
4047
4048\protected\def\newsignal#1%
4049  {\ifdefined#1\else
4050     \advance\maximumsignal 2\scaledpoint % to be save in rounding
4051     \edef#1{\the\maximumsignal}%
4052   \fi}
4053
4054%D \macros
4055%D   {strippedcsname}
4056%D
4057%D The next macro can be very useful when using \type {\csname} like in:
4058%D
4059%D \starttyping
4060%D \csname if\strippedcsname\something\endcsname
4061%D \stoptyping
4062
4063% \def\checkedstrippedcsname#1% this permits \strippedcsname{\xxx} and \strippedcsname{xxx}
4064%   {\expandafter\syst_helpers_checked_stripped_csname\string#1}
4065%
4066% \def\syst_helpers_checked_stripped_csname#1%
4067%  %{\ifx#1\letterbackslash\else#1\fi}
4068%   {\if\noexpand#1\letterbackslash\else#1\fi}
4069
4070\let\checkedstrippedcsname\csstring
4071
4072%D \macros
4073%D   {savenormalmeaning}
4074%D
4075%D We will use this one in:
4076
4077\protected\def\savenormalmeaning#1%
4078  {\ifcsname normal\csstring#1\endcsname \else
4079     \expandafter\let\csname normal\csstring#1\endcsname#1%
4080   \fi}
4081
4082%D \macros
4083%D   {dorecurse,recurselevel,recursedepth,
4084%D    dostepwiserecurse}
4085%D
4086%D \TEX\ does not offer us powerfull for||loop mechanisms. On the other hand its
4087%D recursion engine is quite unique. We therefore identify the for||looping macros
4088%D by this method. The most simple alternative is the one that only needs a number.
4089%D
4090%D \starttyping
4091%D \dorecurse {n} {whatever we want}
4092%D \stoptyping
4093%D
4094%D This macro can be nested without problems and therefore be used in situations
4095%D where \PLAIN\ \TEX's \type {\loop} macro ungracefully fails. The current value of
4096%D the counter is available in \type {\recurselevel}, before as well as after the
4097%D \typ {whatever we wat} stuff.
4098%D
4099%D \starttyping
4100%D \dorecurse               % inner loop
4101%D   {10}
4102%D   {\recurselevel:          % outer value
4103%D      \dorecurse          % inner loop
4104%D        {\recurselevel}     % outer value
4105%D        {\recurselevel}     % inner value
4106%D      \dorecurse          % inner loop
4107%D        {\recurselevel}     % outer value
4108%D        {\recurselevel}     % inner value
4109%D    \endgraf}
4110%D \stoptyping
4111%D
4112%D In this example the first, second and fourth \type {\recurselevel} concern the
4113%D outer loop, while the third and fifth one concern the inner loop. The depth of
4114%D the nesting is available for inspection in \type {\recursedepth}.
4115%D
4116%D Both \type {\recurselevel} and \type {\recursedepth} are macros. The real
4117%D \COUNTERS\ are hidden from the user because we don't want any interference.
4118
4119\newcount\outerrecurse
4120\newcount\innerrecurse
4121
4122\def\recursedepth{\the\outerrecurse}
4123\def\recurselevel{0}
4124
4125\let\syst_helpers_stepwise_next\relax
4126
4127\installsystemnamespace{recurseindex}
4128\installsystemnamespace{recurseaction}
4129
4130\protected\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4
4131  {\global\advance\outerrecurse \plusone
4132   \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname{#4}%
4133   \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel
4134   \ifnum#3>\zerocount\relax
4135     \ifnum#2<#1\relax
4136       \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit
4137     \else
4138       \let\syst_helpers_stepwise_next\syst_helpers_stepwise_recurse
4139     \fi
4140   \else
4141     \ifnum#3<\zerocount\relax
4142       \ifnum#1<#2\relax
4143         \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit
4144       \else
4145         \let\syst_helpers_stepwise_next\syst_helpers_stepwise_reverse
4146       \fi
4147     \else
4148       \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit
4149     \fi
4150   \fi\normalexpanded{\syst_helpers_stepwise_next{\number#1}{\number#2}{\number#3}}}
4151
4152\protected\def\syst_helpers_stepwise_recurse#1#2#3% from to step
4153  {\ifnum#1>#2\relax
4154     \expandafter\syst_helpers_stepwise_recurse_nop
4155   \else
4156     \def\recurselevel{#1}%
4157     \doubleexpandafter\syst_helpers_stepwise_recurse_yes\expandafter
4158   \fi\expandafter{\the\numexpr\recurselevel+#3\relax}{#2}{#3}}
4159
4160\protected\def\syst_helpers_recurse_content
4161  {\csname\??recurseaction\the\outerrecurse\endcsname}
4162
4163\protected\def\syst_helpers_stepwise_recurse_yes
4164  {\syst_helpers_recurse_content
4165   \syst_helpers_stepwise_recurse}
4166
4167\protected\def\syst_helpers_stepwise_reverse#1#2#3% from to step
4168  {\ifnum#1<#2\relax
4169     \expandafter\syst_helpers_stepwise_recurse_nop
4170   \else
4171     \def\recurselevel{#1}%
4172     \innerrecurse#1\relax
4173     \advance\innerrecurse#3\relax
4174     \doubleexpandafter\syst_helpers_stepwise_reverse_yes\expandafter
4175   \fi\expandafter{\the\innerrecurse}{#2}{#3}}
4176
4177\protected\def\syst_helpers_stepwise_reverse_yes
4178  {\syst_helpers_recurse_content
4179   \syst_helpers_stepwise_reverse}
4180
4181\protected\def\syst_helpers_stepwise_exit
4182  {\syst_helpers_stepwise_recurse_nop\relax}
4183
4184\protected\def\syst_helpers_stepwise_recurse_nop#1#2#3#4%
4185  {\expandafter\let\expandafter\recurselevel\csname\??recurseindex\the\outerrecurse\endcsname
4186   \global\advance\outerrecurse\minusone}
4187
4188% \protected\def\nonostepwiserecurse#1#2#3%
4189%   {\expandafter\let\expandafter\recurselevel\csname\??recurseindex\the\outerrecurse\endcsname
4190%    \global\advance\outerrecurse\minusone}
4191
4192\protected\def\dorecurse#1%
4193  {\dostepwiserecurse\plusone{#1}\plusone}
4194
4195\def\doexpandedrecurse#1#2% user macro (also was \doxprecurse)
4196  {\ifnum#1>\zerocount
4197     #2\expandafter\doexpandedrecurse\expandafter{\the\numexpr#1-\plusone\relax}{#2}%
4198   \fi}
4199
4200%D As we can see here, the simple command \type{\dorecurse} is a special case of the
4201%D more general:
4202%D
4203%D \starttyping
4204%D \dostepwiserecurse {from} {to} {step} {action}
4205%D \stoptyping
4206%D
4207%D This commands accepts positive and negative steps. Illegal values are handles as
4208%D good as possible and the macro accepts numbers and \COUNTERS.
4209%D
4210%D \starttyping
4211%D \dostepwiserecurse  {1} {10}  {2} {...}
4212%D \dostepwiserecurse {10}  {1} {-2} {...}
4213%D \stoptyping
4214%D
4215%D Because the simple case is used often, we implement it more efficiently:
4216
4217\protected\def\dorecurse#1%
4218  {\ifcase#1\relax
4219     \expandafter\gobbletwoarguments
4220   \or
4221     \expandafter\syst_helpers_recurse_y
4222   \else
4223     \expandafter\syst_helpers_recurse_x
4224   \fi{#1}}
4225
4226\protected\def\syst_helpers_recurse_x#1#2%
4227  {\global\advance\outerrecurse \plusone
4228   \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname{#2}%
4229   \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel
4230   \expandafter\syst_helpers_recurse_indeed\expandafter1\expandafter{\number#1}}
4231
4232\protected\def\syst_helpers_recurse_y#1#2%
4233  {\global\advance\outerrecurse \plusone
4234   \expandafter\glet\csname\??recurseindex\the\outerrecurse\endcsname\recurselevel
4235   \let\recurselevel\!!plusone
4236   #2%
4237   \expandafter\let\expandafter\recurselevel\csname\??recurseindex\the\outerrecurse\endcsname
4238   \global\advance\outerrecurse \minusone}
4239
4240\protected\def\syst_helpers_recurse_indeed#1#2% from to
4241  {\ifnum#1>#2\relax
4242     \expandafter\syst_helpers_recurse_indeed_nop
4243   \else
4244     \def\recurselevel{#1}%
4245     \doubleexpandafter\syst_helpers_recurse_indeed_yes
4246   \fi\expandafter{\the\numexpr\recurselevel+\plusone\relax}{#2}}
4247
4248\protected\def\syst_helpers_recurse_indeed#1#2% from to
4249  {\ifnum#1>#2\relax
4250     \expandafter\syst_helpers_recurse_indeed_nop
4251   \else
4252     \def\recurselevel{#1}%
4253     \innerrecurse#1\advance\innerrecurse\plusone
4254     \doubleexpandafter\syst_helpers_recurse_indeed_yes
4255   \fi\expandafter{\the\innerrecurse}{#2}}
4256
4257\protected\def\syst_helpers_recurse_indeed_yes
4258  {\syst_helpers_recurse_content
4259   \syst_helpers_recurse_indeed}
4260
4261\protected\def\syst_helpers_recurse_indeed_nop#1#2#3%
4262  {\expandafter\let\expandafter\recurselevel\csname\??recurseindex\the\outerrecurse\endcsname
4263   \global\advance\outerrecurse \minusone }
4264
4265%D \macros
4266%D   {dowith}
4267%D
4268%D Here's a loop over whatever is in a list:
4269%D
4270%D \starttyping
4271%D \dowith{a,b,c}{[#1]}
4272%D \stoptyping
4273
4274\protected\def\dowith#1#2%
4275  {\def\syst_helpers_with##1{#2}%
4276   \normalexpanded{\processcommalist[#1]}\syst_helpers_with}
4277
4278%D \macros
4279%D   {doloop,exitloop}
4280%D
4281%D Sometimes loops are not determined by counters, but by (a combinations of)
4282%D conditions. We therefore implement a straightforward loop, which can only be left
4283%D when we explictly exit it. Nesting is supported. First we present a more
4284%D extensive alternative.
4285%D
4286%D \starttyping
4287%D \doloop
4288%D   {Some kind of typesetting punishment \par
4289%D    \ifnum\pageno>100 \exitloop \fi}
4290%D \stoptyping
4291%D
4292%D When needed, one can call for \type {\looplevel} and \type {\loopdepth}.
4293
4294\let\endofloop\donothing % maybe \syst_helpers_loop_end
4295
4296\protected\def\doloop#1%
4297  {\global\advance\outerrecurse \plusone
4298   \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname{#1}%
4299   \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel
4300   \let\endofloop\syst_helpers_loop
4301   \syst_helpers_loop1} % no \plusone else \recurselevel wrong
4302
4303\protected\def\syst_helpers_loop#1%
4304  {\def\recurselevel{#1}%
4305   \expandafter\syst_helpers_loop_yes\expandafter{\the\numexpr\recurselevel+\plusone\relax}}
4306
4307\protected\def\syst_helpers_loop_yes
4308  {\syst_helpers_recurse_content
4309   \endofloop}
4310
4311\protected\def\syst_helpers_loop_nop#1%
4312  {\let\endofloop\syst_helpers_loop % new, permits nested \doloop's
4313   \expandafter\let\expandafter\recurselevel\csname\??recurseindex\the\outerrecurse\endcsname
4314   \global\advance\outerrecurse\minusone}
4315
4316\protected\def\exitloop                     % \exitloop quits at end
4317  {\let\endofloop\syst_helpers_loop_nop}
4318
4319\protected\def\exitloopnow#1\endofloop % \exitloopnow quits directly
4320  {\syst_helpers_loop_nop}
4321
4322%D The loop is executed at least once, so beware of situations
4323%D like:
4324%D
4325%D \starttyping
4326%D \doloop {\exitloop some commands}
4327%D \stoptyping
4328%D
4329%D It's just a matter of putting the text into the \type {\if} statement that should
4330%D be there anyway, like in:
4331%D
4332%D \starttyping
4333%D \doloop {\ifwhatever \exitloop \else some commands\fi}
4334%D \stoptyping
4335%D
4336%D You can also quit a loop immediately, by using \type
4337%D {\exitloopnow} instead. Beware, this is more sensitive
4338%D for conditional errors.
4339
4340%D Krzysztof Leszczynski suggested to provide access to the level by means of a
4341%D \type {#1}. I decided to pass the more frequently used level as \type {#1} and
4342%D the less favoured depth as \type {#2}. The intended usage is:
4343%D
4344%D \starttyping
4345%D \dorecurse{3}{\definesymbol[test-#1][xx-#1]}
4346%D
4347%D \def\test{\dorecurse{3}{\definesymbol[test-##1][xx-##1]}} \test
4348%D
4349%D \symbol[test-1]\quad\symbol[test-2]\quad\symbol[test-3]
4350%D \stoptyping
4351%D
4352%D Since the hashed arguments are expanded, we don't need tricky expansion here.
4353%D
4354%D \starttyping
4355%D \dorecurse{3}{\expanded{\definesymbol[test-\recurselevel][xx-\recurselevel]}}
4356%D \stoptyping
4357
4358\def\syst_helpers_recurse_content
4359  {\csname\??recurseaction\the\outerrecurse\expandafter\expandafter\expandafter\endcsname
4360     \expandafter\expandafter\expandafter{\expandafter\recurselevel\expandafter}\expandafter{\the\outerrecurse}}
4361
4362\protected\def\syst_helpers_recurse_x#1#2%
4363  {\global\advance\outerrecurse \plusone
4364   \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#2}%
4365   \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel
4366   \expandafter\syst_helpers_recurse_indeed\expandafter1\expandafter{\number#1}}
4367
4368\protected\def\syst_helpers_recurse_y#1#2%
4369  {\global\advance\outerrecurse \plusone
4370   \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel
4371   \let\recurselevel\!!plusone
4372   \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#2}%
4373   \syst_helpers_recurse_content
4374   \expandafter\let\expandafter\recurselevel\csname\??recurseindex\the\outerrecurse\endcsname
4375   \global\advance\outerrecurse \minusone}
4376
4377\protected\def\doloop#1%
4378  {\global\advance\outerrecurse \plusone
4379   \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#1}%
4380   \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel
4381   \let\endofloop\syst_helpers_loop
4382   \syst_helpers_loop1} % no \plusone else \recurselevel wrong
4383
4384% for instance:
4385%
4386% \protected\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4
4387%   {\global\advance\outerrecurse \plusone
4388%    \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#4}%
4389%    \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel
4390%    \ifnum#3>\zerocount\relax
4391%      \ifnum#2<#1\relax
4392%        \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit
4393%      \else
4394%        \let\syst_helpers_stepwise_next\syst_helpers_stepwise_recurse
4395%      \fi
4396%    \else
4397%      \ifnum#3<\zerocount\relax
4398%        \ifnum#1<#2\relax
4399%          \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit
4400%        \else
4401%          \let\syst_helpers_stepwise_next\syst_helpers_stepwise_reverse
4402%        \fi
4403%      \else
4404%        \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit
4405%      \fi
4406%    \fi\normalexpanded{\syst_helpers_stepwise_next{\number#1}{\number#2}{\number#3}}}
4407%
4408% faster:
4409%
4410% \protected\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4
4411%   {\global\advance\outerrecurse \plusone
4412%    \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#4}%
4413%    \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel
4414%    \csname @swr%
4415%      \ifnum#3>\zerocount
4416%        \ifnum#2<#1\else d\fi
4417%      \else\ifnum#3<\zerocount
4418%        \ifnum#1<#2\else r\fi
4419%      \fi\fi
4420%    \expandafter\endcsname\normalexpanded{{\number#1}{\number#2}{\number#3}}}
4421%
4422% \let\@swr \syst_helpers_stepwise_exit
4423% \let\@swrd\syst_helpers_stepwise_recurse
4424% \let\@swrr\syst_helpers_stepwise_reverse
4425%
4426% nicer:
4427
4428\installsystemnamespace{recursestepwise}
4429
4430\protected\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4
4431  {\global\advance\outerrecurse \plusone
4432   \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#4}%
4433   \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel
4434   \csname\??recursestepwise
4435     % we need the x in order to avoid the \relax that tex adds
4436     \ifnum#3>\zerocount
4437       \ifnum#2<#1x\else d\fi
4438     \else\ifnum#3<\zerocount
4439       \ifnum#1<#2x\else r\fi
4440     \else
4441       x%
4442     \fi\fi
4443   \expandafter\endcsname\normalexpanded{{\number#1}{\number#2}{\number#3}}}
4444 % \expandafter\endcsname\expandafter{\number#1\expandafter}\expandafter{\number#2\expandafter}\expandafter{\number#3}}
4445
4446\letvalue{\??recursestepwise x}\syst_helpers_stepwise_exit
4447\letvalue{\??recursestepwise d}\syst_helpers_stepwise_recurse
4448\letvalue{\??recursestepwise r}\syst_helpers_stepwise_reverse
4449
4450% quite okay too, but untested
4451%
4452% \def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4
4453%   {\global\advance\outerrecurse \plusone
4454%    \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#4}%
4455%    \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel
4456%    \normalexpanded
4457%      {\ifnum#3>\zerocount
4458%         \ifnum#2<#1
4459%           \syst_helpers_stepwise_exit
4460%         \else
4461%           \syst_helpers_stepwise_recurse
4462%         \fi
4463%       \else
4464%         \ifnum#3<\zerocount
4465%           \ifnum#1<#2
4466%             \syst_helpers_stepwise_exit
4467%           \else
4468%             \syst_helpers_stepwise_reverse
4469%           \fi
4470%         \else
4471%           \syst_helpers_stepwise_exit
4472%         \fi
4473%       \fi{\number#1}{\number#2}{\number#3}}}
4474
4475%D For special purposes:
4476
4477\newcount\fastloopindex
4478\newcount\fastloopfinal
4479
4480\let\m_syst_helpers_fast_loop_cs\relax
4481
4482\protected\def\dofastloopcs#1%
4483  {\fastloopfinal#1\relax
4484   \ifcase\fastloopfinal
4485     \expandafter\gobbleoneargument
4486   \else
4487     \expandafter\syst_helpers_fast_loop_cs
4488   \fi}
4489
4490\protected\def\syst_helpers_fast_loop_cs#1%
4491  {\let\m_syst_helpers_fast_loop_cs#1%
4492   \fastloopindex\plusone
4493   \syst_helpers_fast_loop_cs_step}
4494
4495\protected\def\syst_helpers_fast_loop_cs_step
4496  {\ifnum\fastloopindex>\fastloopfinal
4497     \let\m_syst_helpers_fast_loop_cs\relax
4498   \else
4499     \m_syst_helpers_fast_loop_cs
4500     \advance\fastloopindex\plusone
4501     \expandafter\syst_helpers_fast_loop_cs_step
4502   \fi}
4503
4504% Helper:
4505
4506\protected\def\resetrecurselevel{\let\recurselevel\!!zerocount}
4507
4508\let\recurselevel \!!zerocount
4509\let\recurseaction\relax
4510\let\recursestring\empty
4511
4512% \appendtoks \resetrecurselevel \to \everydump
4513
4514%D \macros
4515%D   {doloopoverlist}
4516%D
4517%D \starttyping
4518%D \doloopoverlist {red,green,blue} {
4519%D      \setuppalet[\recursestring]
4520%D      \doloopoverlist {light,normal,dark} {
4521%D          \blackrule[color=\recursestring,width=20cm,height=2cm,depth=0cm]\par
4522%D      }
4523%D  }
4524%D \stoptyping
4525%D
4526%D or:
4527%D
4528%D \starttyping
4529%D \doloopoverlist {red,green,blue} {
4530%D      \setuppalet[#1]
4531%D      \doloopoverlist {light,normal,dark} {
4532%D          \blackrule[color=##1,width=20cm,height=2cm,depth=0cm]\par
4533%D      }
4534%D  }
4535%D \stoptyping
4536
4537\protected\def\doloopoverlist#1#2%
4538  {\global\advance\outerrecurse\plusone
4539   \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1{\edef\recursestring{##1}#2}%
4540   \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recursestring
4541   \normalexpanded{\processcommalist[#1]{\expandafter\noexpand\csname\??recurseaction\the\outerrecurse\endcsname}}%
4542   \expandafter\let\expandafter\recursestring\csname\??recurseindex\the\outerrecurse\endcsname
4543   \global\advance\outerrecurse\minusone}
4544
4545%D \macros
4546%D   {newevery,everyline,EveryLine,EveryPar}
4547%D
4548%D Lets skip to something quite different. It's common use to use \type {\everypar}
4549%D for special purposes. In \CONTEXT\ we use this primitive for locating sidefloats.
4550%D This means that when user assignments to \type {\everypar} can interfere with
4551%D those of the package. We therefore introduce \type {\EveryPar}.
4552%D
4553%D The same goes for \type {\EveryLine}. Because \TEX\ offers no \type {\everyline}
4554%D primitive, we have to call for \type {\everyline} when we are working on a line
4555%D by line basis. Just by calling \type {\EveryPar{}} and \type {\EveryLine{}} we
4556%D restore the old situation.
4557
4558% \dorecurse{2}{
4559%     \expanded{\everypar{before \recurselevel\space}}
4560%     \EveryPar{x } [before \recurselevel\space x] \par
4561%     \EveryPar{y } [before \recurselevel\space y] \par
4562%     \EveryPar{}   [before \recurselevel]   \par
4563%     \EveryPar{x } \EveryPar{y } \EveryPar{} [before \recurselevel] \par
4564%     \EveryPar{y } \everypar{before } [before] \par
4565% }
4566
4567\installsystemnamespace{extraevery}
4568
4569% \protected\def\newevery#1#2%
4570%   {\ifx#1\everypar\else\newtoks#1\fi% we test for redefinition elsewhere
4571%    \ifx#2\relax\else\ifdefined#2\else
4572%      \expandafter\newtoks\csname\??extraevery\csstring#1\endcsname
4573%      \def#2{\syst_helpers_every#1}%
4574%    \fi\fi}
4575%
4576% \protected\def\syst_helpers_every#1%
4577%   {\expandafter\removetoks\expandafter\the\csname\??extraevery\csstring#1\endcsname\from#1%
4578%    \expandafter\appendtoks\expandafter\the\csname\??extraevery\csstring#1\endcsname\to  #1%
4579%    \csname\??extraevery\csstring#1\endcsname}
4580
4581\protected\def\newevery#1#2%
4582  {\ifx#1\everypar\else\newtoks#1\fi% we test for redefinition elsewhere
4583   \ifx#2\relax\else\ifdefined#2\else
4584     \expandafter\newtoks\csname\??extraevery\csstring#1\endcsname
4585     \edef#2{\syst_helpers_every#1\csname\??extraevery\csstring#1\endcsname}%
4586   \fi\fi}
4587
4588\protected\def\syst_helpers_every#1#2%
4589  {\removetoks\the#2\from#1%
4590   \appendtoks\the#2\to  #1%
4591   #2}
4592
4593%D This one permits definitions like:
4594
4595\newevery \everypar  \EveryPar  % we get a warning which is ok
4596\newevery \everyline \EveryLine
4597
4598%D and how about:
4599
4600% \newtoks \neverypar
4601% \newtoks \neveryendpar
4602%
4603% \protected\def\syst_helpers_forgotten_endpar
4604%   {\the\neveryendpar\normalpar}
4605%
4606% \protected\def\forgeteverypar
4607%   {\everypar{\the\neverypar}%
4608%    \let\endpar\syst_helpers_forgotten_endpar}
4609%
4610% \protected\def\finishpar
4611%   {\ifvmode\else\par\fi}
4612
4613\newtoks \neverypar
4614
4615\protected\def\forgeteverypar
4616  {\everypar{\the\neverypar}}
4617
4618%D Which we're going to use indeed! When the second argument equals \type {\relax},
4619%D the first token list is created unless it is already defined.
4620%D
4621%D Technically spoken we could have used the method we are going to present in the
4622%D visual debugger. First we save the primitive \type{\everypar}:
4623%D
4624%D \starttyping
4625%D \let\normaleverypar=\everypar
4626%D \stoptyping
4627%D
4628%D Next we allocate a \TOKENLIST\ named \type{\everypar}, which means that
4629%D \type{\everypar} is no longer a primitive but something like \type{\toks44}.
4630%D
4631%D \starttyping
4632%D \newtoks\everypar
4633%D \stoptyping
4634%D
4635%D Because \TEX\ now executes \type{\normaleverypar} instead of \type{\everypar}, we
4636%D are ready to assign some tokens to this internally known and used \TOKENLIST.
4637%D
4638%D \starttyping
4639%D \normaleverypar={all the things the system wants to do \the\everypar}
4640%D \stoptyping
4641%D
4642%D Where the user can provide his own tokens to be expanded every time he expects
4643%D them to expand.
4644%D
4645%D \starttyping
4646%D \everypar={something the user wants to do}
4647%D \stoptyping
4648%D
4649%D We don't use this method because it undoubtly leads to confusing situations,
4650%D especially when other packages are used, but it's this kind of tricks that make
4651%D \TEX\ so powerful.
4652
4653%D \macros
4654%D   {convertargument,convertcommand,convertvalue}
4655%D
4656%D Some persistent experimenting led us to the next macro. This macro converts a
4657%D parameter or an expanded macro to it's textual meaning.
4658%D
4659%D \starttyping
4660%D \convertargument ... \to \command
4661%D \stoptyping
4662%D
4663%D For example,
4664%D
4665%D \starttyping
4666%D \convertargument{one \two \three{four}}\to\ascii
4667%D \stoptyping
4668%D
4669%D The resulting macro \type{\ascii} can be written to a file or the terminal
4670%D without problems. In \CONTEXT\ we use this macro for generating registers and
4671%D tables of contents.
4672%D
4673%D The second conversion alternative accepts a command:
4674%D
4675%D \starttyping
4676%D \convertcommand\command\to\ascii
4677%D \stoptyping
4678%D
4679%D Both commands accept the prefix \type{\doglobal} for global assignments.
4680
4681\protected\def\convertvalue#1\to
4682  {\expandafter\convertcommand\csname#1\endcsname\to}
4683
4684\protected\def\defconvertedvalue#1#2% less sensitive for \to
4685  {\expandafter\defconvertedcommand\expandafter#1\csname#2\endcsname}
4686
4687%D \macros
4688%D   {doifassignmentelse}
4689%D
4690%D A lot of \CONTEXT\ commands take optional arguments, for instance:
4691%D
4692%D \starttyping
4693%D \dothisorthat[alfa,beta]
4694%D \dothisorthat[first=foo,second=bar]
4695%D \dothisorthat[alfa,beta][first=foo,second=bar]
4696%D \stoptyping
4697%D
4698%D Although a combined solution is possible, we prefer a seperation. The next
4699%D command takes care of propper handling of such multi||faced commands.
4700%D
4701%D \starttyping
4702%D \doifassignmentelse {...} {then ...} {else ...}
4703%D \stoptyping
4704
4705\def\syst_helpers_check_if_assignment_else#1=#2#3^^^^0004{\if#2^^^^0003}%
4706\def\syst_helpers_check_else_assignment_if#1=#2#3^^^^0004{\unless\if#2^^^^0003}%
4707
4708\protected\def\doifelseassignment#1%
4709  {\expandafter\syst_helpers_check_if_assignment_else\detokenize{#1}=^^^^0003^^^^0003^^^^0004%
4710     \expandafter\secondoftwoarguments
4711   \else
4712     \expandafter\firstoftwoarguments
4713   \fi}
4714
4715\protected\def\doifelseassignmentcs#1#2#3%
4716  {\expandafter\syst_helpers_check_if_assignment_else\detokenize{#1}=^^^^0003^^^^0003^^^^0004%
4717     \expandafter#3%
4718   \else
4719     \expandafter#2%
4720   \fi}
4721
4722\let\doifassignmentelse  \doifelseassignment
4723\let\doifassignmentelsecs\doifelseassignmentcs
4724
4725\newif\ifassignment
4726
4727\protected\def\docheckassignment#1%
4728  {\expandafter\syst_helpers_check_if_assignment_else\detokenize{#1}=^^^^0003^^^^0003^^^^0004%
4729     \assignmentfalse
4730   \else
4731     \assignmenttrue
4732   \fi}
4733
4734%D These can be used for cases where we want less tracing noise.
4735
4736\protected\def\validassignment#1%
4737  {\expandafter\syst_helpers_check_else_assignment_if\detokenize{#1}=^^^^0003^^^^0003^^^^0004}
4738
4739\protected\def\novalidassignment#1%
4740  {\expandafter\syst_helpers_check_if_assignment_else\detokenize{#1}=^^^^0003^^^^0003^^^^0004}
4741
4742%D In \ETEX\ we can use \type {\detokenize} and gain some speed, but in general far
4743%D less that 1\% for \type {\convertargument} and nil for \type {\convertcommand}.
4744%D This macro is more robust than the pure \TEX\ one, something I found out when
4745%D primitives like \type {\jobname} were fed (or something undefined).
4746
4747\protected\def\convertargument#1\to#2{\dodoglobal\edef#2{\detokenize{#1}}}
4748\protected\def\convertcommand #1\to#2{\dodoglobal\edef#2{\expandafter\detokenize\expandafter{#1}}} % hm, only second is also ok
4749
4750\protected\def\defconvertedargument #1#2{\edef#1{\detokenize{#2}}}
4751\protected\def\defconvertedcommand  #1#2{\edef#1{\detokenize\expandafter{#2}}}
4752\protected\def\edefconvertedargument#1#2{\edef#1{#2}%
4753                                          \edef#1{\detokenize\expandafter{#1}}}
4754\protected\def\gdefconvertedargument#1#2{\xdef#1{\detokenize{#2}}}
4755\protected\def\gdefconvertedcommand #1#2{\xdef#1{\detokenize\expandafter{#2}}}
4756\protected\def\xdefconvertedargument#1#2{\xdef#1{#2}%
4757                                          \xdef#1{\detokenize\expandafter{#1}}}
4758
4759%D When you try to convert a primitive command, you'll find out that the \ETEX\
4760%D method fails on for instance \type {\jobname} in the sense that it returns the
4761%D filename instead of just \type {\jobname}. So far this does not give real
4762%D problems.
4763
4764%D This is typically a macro that one comes to after reading the \TEX book
4765%D carefully. Even then, the definite solution was found after rereading the \TEX
4766%D book. The first implementation was:
4767%D
4768%D \starttyping
4769%D \def\doconvertargument#1->#2\\\\{#2}
4770%D \stoptyping
4771%D
4772%D The \type {-}, the delimiter \type {\\\\} and the the second argument are
4773%D completely redundant.
4774
4775%D \macros
4776%D   {showvalue}
4777%D
4778%D Ahandy macro, for testing purposes only:
4779
4780\protected\def\showvalue#1%
4781  {\ifcsname#1\endcsname
4782     \expandafter\show\csname#1\endcsname
4783   \else
4784     \show\undefined
4785   \fi}
4786
4787%D \macros
4788%D   {doifmeaningelse}
4789%D
4790%D We can use both commands in testing, but alas, not all meanings expand to
4791%D something \type {->}. This is no problem in the \ETEX\ implementation, but since
4792%D we want compatibility, we need:
4793%D
4794%D \starttyping
4795%D \doifmeaningelse {\next} {\something} {true} {false}
4796%D \stoptyping
4797%D
4798%D Watch the one level expansion of the second argument.
4799
4800\protected\def\doifelsemeaning#1#2%
4801  {\edef\m_syst_string_one{\normalmeaning#1}%
4802   \def \m_syst_string_two{#2}%
4803   \edef\m_syst_string_two{\normalmeaning\m_syst_string_two}%
4804   \ifx\m_syst_string_one\m_syst_string_two
4805     \expandafter\firstoftwoarguments
4806   \else
4807     \expandafter\secondoftwoarguments
4808   \fi}
4809
4810\let\doifmeaningelse\doifelsemeaning
4811
4812%D \macros
4813%D   {doifsamestringselse,doifsamestring,doifnotsamestring}
4814%D
4815%D The next comparison macro converts the arguments into expanded strings. This
4816%D command can be used to compare for instance \type {\jobname} with a name stored
4817%D in a macro.
4818%D
4819%D \starttyping
4820%D \doifelse          {\jobname}{oeps}{YES}{NO}
4821%D \doifsamestringelse{\jobname}{oeps}{YES}{NO}
4822%D \stoptyping
4823
4824\def\syst_helpers_if_samestring_else#1#2#3#4%
4825  {\edef\m_syst_string_one{\detokenize\expandafter{\normalexpanded{#3}}}%
4826   \edef\m_syst_string_two{\detokenize\expandafter{\normalexpanded{#4}}}%
4827   \ifx\m_syst_string_one\m_syst_string_two\expandafter#1\else\expandafter#2\fi}
4828
4829\protected\def\doifelsesamestring{\syst_helpers_if_samestring_else\firstoftwoarguments\secondoftwoarguments}
4830\protected\def\doifsamestring    {\syst_helpers_if_samestring_else\firstofoneargument \gobbleoneargument   }
4831\protected\def\doifnotsamestring {\syst_helpers_if_samestring_else\gobbleoneargument  \firstofoneargument  }
4832
4833\let\doifsamestringelse\doifelsesamestring
4834
4835%D \macros
4836%D   {ConvertToConstant,ConvertConstantAfter}
4837%D
4838%D When comparing arguments with a constant, we can get into trouble when this
4839%D argument consists of tricky expandable commands. One solution for this is
4840%D converting the argument to a string of unexpandable characters. To make
4841%D comparison possible, we have to convert the constant too.
4842%D
4843%D \starttyping
4844%D \ConvertToConstant\doifelse {...} {...} {then ...} {else ...}
4845%D \stoptyping
4846%D
4847%D This construction is only needed when the first argument can give troubles.
4848%D Misuse can slow down processing.
4849%D
4850%D \starttyping
4851%D \ConvertToConstant\doifelse{\c!alfa}        {\c!alfa}{...}{...}
4852%D \ConvertToConstant\doifelse{alfa}           {\c!alfa}{...}{...}
4853%D \ConvertToConstant\doifelse{alfa}           {alfa}   {...}{...}
4854%D \ConvertToConstant\doifelse{alfa \alfa test}{\c!alfa}{...}{...}
4855%D \stoptyping
4856%D
4857%D In examples~2 and~3 both arguments equal, in~1 and~4 they differ.
4858
4859\protected\def\ConvertToConstant#1#2#3%
4860  {\edef\m_syst_string_one{\expandafter\detokenize\expandafter{#2}}%
4861   \edef\m_syst_string_two{\expandafter\detokenize\expandafter{#3}}%
4862   #1{\m_syst_string_one}{\m_syst_string_two}}
4863
4864%D When the argument \type{#1} consists of commands, we had better use
4865%D
4866%D \starttyping
4867%D \ConvertConstantAfter\processaction[#1][...]
4868%D \ConvertConstantAfter\doifelse{#1}{\v!something}{}{}
4869%D \stoptyping
4870%D
4871%D This commands accepts things like:
4872%D
4873%D \starttyping
4874%D \v!constant
4875%D constant
4876%D \hbox to \hsize{\rubish}
4877%D \stoptyping
4878%D
4879%D As we will see in the core modules, this macro permits constructions like:
4880%D
4881%D \starttyping
4882%D \setupfootertexts[...][...]
4883%D \setupfootertexts[margin][...][...]
4884%D \setupfootertexts[\v!margin][...][...]
4885%D \stoptyping
4886%D
4887%D where \type {...} can be anything legally \TEX.
4888
4889\protected\def\CheckConstantAfter#1#2%
4890  {\expandafter\convertargument\v!prefix!\to\ascii
4891   \convertargument#1\to#2\relax
4892   \doifelseinstring\ascii{#2}
4893     {\expandafter\convertargument#1\to#2}
4894     {}}
4895
4896\protected\def\ConvertConstantAfter#1#2#3%
4897  {\CheckConstantAfter{#2}\asciia
4898   \CheckConstantAfter{#3}\asciib
4899   #1{\asciia}{\asciib}}
4900
4901%D \macros
4902%D   {assignifempty}
4903%D
4904%D We can assign a default value to an empty macro using:
4905%D
4906%D \starttyping
4907%D \assignifempty \macros {default value}
4908%D \stoptyping
4909%D
4910%D We don't explicitly test if the macro is defined.
4911
4912\protected\def\assignifempty#1#2% can be sped up
4913  {\doifnothing{#1}{\def#1{#2}}}
4914
4915%D \macros
4916%D   {gobbleuntil,grabuntil,gobbleuntilrelax,
4917%D    processbetween,processuntil}
4918%D
4919%D In \TEX\ gobbling usually stand for skipping arguments, so here are our gobbling
4920%D macros.
4921%D
4922%D In \CONTEXT\ we use a lot of \type {\start}||\type {\stop} like constructions.
4923%D Sometimes, the \type {\stop} is used as a hard coded delimiter like in: %D
4924%D \starttyping
4925%D \protected\def\startcommand#1\stopcommand%
4926%D   {... #1 ...}
4927%D \stoptyping
4928%D
4929%D In many cases the \type {\start}||\type {\stop} pair is defined at format
4930%D generation time or during a job. This means that we cannot hardcode the \type
4931%D {\stop} criterium. Only after completely understanding \type {\csname} and \type
4932%D {\expandafter} I was able to to implement a solution, starting with:
4933%D
4934%D \starttyping
4935%D \grabuntil{stop}\command
4936%D \stoptyping
4937%D
4938%D This commands executes, after having encountered \type {\stop} the command \type
4939%D {\command}. This command receives as argument the text preceding the \type
4940%D {\stop}. This means that:
4941%D
4942%D \starttyping
4943%D \protected\def\starthello%
4944%D   {\grabuntil{stophello}\message}
4945%D
4946%D \starthello Hello world!\stophello
4947%D \stoptyping
4948%D
4949%D results in: \type{\message{Hello world!}}.
4950
4951\let\syst_helpers_grab_indeed\relax
4952
4953\protected\def\syst_helpers_grab#1#2%
4954  {\def\syst_helpers_grab_indeed##1#1{#2{##1}}\syst_helpers_grab_indeed}
4955
4956\protected\def\grabuntil#1%
4957  {\expandafter\syst_helpers_grab\expandafter{\csname#1\endcsname}}
4958
4959%D The next command build on this mechanism:
4960%D
4961%D \starttyping
4962%D \processbetween{string}\command
4963%D \stoptyping
4964%D
4965%D Here:
4966%D
4967%D \starttyping
4968%D \processbetween{hello}\message
4969%D \starthello Hello again!\stophello
4970%D \stoptyping
4971%D
4972%D leads to: \type{\message{Hello again!}}. The command
4973%D
4974%D \starttyping
4975%D \gobbleuntil{sequence}
4976%D \stoptyping
4977%D
4978%D is related to these commands. This one simply throws away
4979%D everything preceding \type{\command}.
4980
4981\let\syst_helpers_gobble_indeed\relax
4982
4983\protected\def\processbetween#1#2%
4984  {\setvalue{\s!start#1}{\grabuntil{\s!stop#1}{#2}}}
4985
4986\protected\def\gobbleuntil#1%
4987  {\def\syst_helpers_gobble_indeed##1#1{}\syst_helpers_gobble_indeed}
4988
4989\protected\def\gobbleuntilrelax#1\relax
4990  {}
4991
4992% experimental
4993
4994\protected\def\gobblenested#1#2#3%
4995  {\normalexpanded{\def\noexpand\next##1\csname#2\endcsname}{\csname#3\endcsname}%
4996   \next}%
4997
4998%D The next one simply expands the pickup up tokens.
4999%D
5000%D \starttyping
5001%D \processuntil{sequence}
5002%D \stoptyping
5003
5004\let\syst_helpers_until_indeed\relax
5005
5006\protected\def\processuntil#1%
5007  {\def\syst_helpers_until_indeed##1#1{##1}\syst_helpers_until_indeed}
5008
5009%D \macros
5010%D   {groupedcommand}
5011%D
5012%D Commands often manipulate argument as in:
5013%D
5014%D \starttyping
5015%D \def\doezomaarwat#1{....#1....}
5016%D \stoptyping
5017%D
5018%D A disadvantage of this approach is that the tokens that form \type{#1} are fixed
5019%D the the moment the argument is read in. Normally this is no problem, but for
5020%D instance verbatim environments adapt the \CATCODES\ of characters and therefore
5021%D are not always happy with already fixed tokens.
5022%D
5023%D Another problem arises when the argument is grouped not by \type {{}} but by
5024%D \type {\bgroup} and \type {\egroup}. Such an argument fails, because the \type
5025%D {\bgroup} is een as the argument (which is quite normal).
5026%D
5027%D The next macro offers a solution for both unwanted
5028%D situations:
5029%D
5030%D \starttyping
5031%D \groupedcommand {before} {after}
5032%D \stoptyping
5033%D
5034%D Which can be used like:
5035%D
5036%D \starttyping
5037%D \def\cite%
5038%D   {\groupedcommand{\rightquote\rightquote}{\leftquote\leftquote}}
5039%D \stoptyping
5040%D
5041%D This command is equivalent to, but more 'robust' than:
5042%D
5043%D \starttyping
5044%D \def\cite#1%
5045%D   {\rightquote\rightquote#1\leftquote\leftquote}
5046%D \stoptyping
5047%D
5048%D \starttyping
5049%D \def\rightword%
5050%D   {\groupedcommand{\hfill\hbox}{\parfillskip\zeropoint}}
5051%D
5052%D .......... \rightword{the right way}
5053%D \stoptyping
5054%D
5055%D Here \TEX\ typesets \type {\bf the right way} unbreakable at the end of the line.
5056%D The solution mentioned before does not work here. We also handle
5057%D
5058%D \starttyping
5059%D to be \bold{bold} or not, that's the question
5060%D \stoptyping
5061%D
5062%D and
5063%D
5064%D \starttyping
5065%D to be {\bold bold} or not, that's the question
5066%D \stoptyping
5067%D
5068%D This alternative checks for a \type {\bgroup} token first. The internal
5069%D alternative does not accept the box handling mentioned before, but further
5070%D nesting works all right. The extra \type {\bgroup}||\type {\egroup} is needed to
5071%D keep \type {\m_syst_helpers_handle_group_after} both into sight and local.
5072
5073\let\m_syst_helpers_handle_group_after \relax
5074\let\m_syst_helpers_handle_group_before\relax
5075
5076% keep:
5077%
5078% \protected\def\syst_helpers_handle_group_normal#1#2%
5079%   {\bgroup
5080%    \def\m_syst_helpers_handle_group_before{\bgroup#1\bgroup\aftergroup\m_syst_helpers_handle_group_after}% can't we remove the second \bgroup
5081%    \def\m_syst_helpers_handle_group_after {#2\egroup\egroup}% and one \egroup here?
5082%    \afterassignment\m_syst_helpers_handle_group_before
5083%    \let\next=}
5084
5085% \protected\def\syst_helpers_handle_group_normal#1#2%
5086%   {\bgroup
5087%    \def\m_syst_helpers_handle_group_before{#1}%
5088%    \def\m_syst_helpers_handle_group_after {#2}%
5089%    \afterassignment\m_syst_helpers_handle_group_normal_before
5090%    \let\next=}
5091%
5092% \def\m_syst_helpers_handle_group_normal_before
5093%   {\bgroup
5094%    \m_syst_helpers_handle_group_before
5095%    \bgroup
5096%    \aftergroup\m_syst_helpers_handle_group_normal_after}
5097%
5098% \def\m_syst_helpers_handle_group_normal_after
5099%   {\m_syst_helpers_handle_group_after
5100%    \egroup
5101%    \egroup}
5102%
5103% \protected\def\syst_helpers_handle_group_simple#1#2% no inner group (so no kerning interference)
5104%   {\bgroup
5105%    \def\m_syst_helpers_handle_group_before{#1}%
5106%    \def\m_syst_helpers_handle_group_after {#2}%
5107%    \afterassignment\m_syst_helpers_handle_group_simple_before
5108%    \let\next=}
5109%
5110% \def\m_syst_helpers_handle_group_simple_before
5111%   {\bgroup
5112%    \aftergroup\m_syst_helpers_handle_group_simple_after
5113%    \m_syst_helpers_handle_group_before}
5114%
5115% \def\m_syst_helpers_handle_group_simple_after
5116%   {\m_syst_helpers_handle_group_after
5117%    \egroup}%
5118%
5119% \protected\def\syst_helpers_handle_group_pickup#1#2#3% no inner group (so no kerning interference)
5120%   {\bgroup
5121%    \def\m_syst_helpers_handle_group_before{#1}%
5122%    \def\m_syst_helpers_handle_group_after {#2\egroup#3}%
5123%    \afterassignment\m_syst_helpers_handle_group_pickup_before
5124%    \let\next=}
5125%
5126% \def\m_syst_helpers_handle_group_pickup_before
5127%   {\bgroup
5128%    \aftergroup\m_syst_helpers_handle_group_after
5129%    \m_syst_helpers_handle_group_before}
5130%
5131% \protected\def\syst_helpers_handle_group_nop
5132%   {\ifnum\currentgrouptype=\semisimplegroupcode
5133%      \expandafter\syst_helpers_handle_group_nop_a
5134%    \else
5135%      \expandafter\syst_helpers_handle_group_nop_b
5136%    \fi}
5137%
5138% \def\syst_helpers_handle_group_nop_a#1#2%
5139%   {\def\m_syst_helpers_handle_group_after{#2\endgroup}%
5140%    \begingroup
5141%    \aftergroup\m_syst_helpers_handle_group_after
5142%    #1}
5143%
5144% \def\syst_helpers_handle_group_nop_b#1#2%
5145%   {\def\m_syst_helpers_handle_group_after{#2\egroup}%
5146%    \bgroup
5147%    \aftergroup\m_syst_helpers_handle_group_after
5148%    #1}
5149
5150\protected\def\syst_helpers_handle_group_nop
5151  {\ifnum\currentgrouptype=\semisimplegroupcode
5152     \expandafter\syst_helpers_handle_group_nop_a
5153   \else
5154     \expandafter\syst_helpers_handle_group_nop_b
5155   \fi}
5156
5157\def\syst_helpers_handle_group_nop_a
5158  {\begingroup
5159   \aftergroup\m_syst_helpers_handle_group_a
5160   \aftergroup\endgroup
5161   \m_syst_helpers_handle_group_b}
5162
5163\def\syst_helpers_handle_group_nop_b
5164  {\bgroup
5165   \aftergroup\m_syst_helpers_handle_group_a
5166   \aftergroup\egroup
5167   \m_syst_helpers_handle_group_b}
5168
5169\protected\def\syst_helpers_handle_group_normal
5170  {\bgroup
5171   \afterassignment\m_syst_helpers_handle_group_normal_before
5172   \let\next=}
5173
5174\def\m_syst_helpers_handle_group_normal_before
5175  {\bgroup
5176   \m_syst_helpers_handle_group_b
5177   \bgroup
5178   \aftergroup\m_syst_helpers_handle_group_a
5179   \aftergroup\egroup
5180   \aftergroup\egroup}
5181
5182\protected\def\syst_helpers_handle_group_simple% no inner group (so no kerning interference)
5183  {\bgroup
5184   \afterassignment\m_syst_helpers_handle_group_simple_before
5185   \let\next=}
5186
5187\def\m_syst_helpers_handle_group_simple_before
5188  {\bgroup
5189   \aftergroup\m_syst_helpers_handle_group_simple_after
5190   \m_syst_helpers_handle_group_b}
5191
5192\def\m_syst_helpers_handle_group_simple_after
5193  {\m_syst_helpers_handle_group_a
5194   \egroup}%
5195
5196\protected\def\syst_helpers_handle_group_pickup% no inner group (so no kerning interference)
5197  {\bgroup
5198   \afterassignment\m_syst_helpers_handle_group_pickup_before
5199   \let\next=}
5200
5201\def\m_syst_helpers_handle_group_pickup_before
5202  {\bgroup
5203   \aftergroup\m_syst_helpers_handle_group_a
5204   \aftergroup\egroup
5205   \aftergroup\m_syst_helpers_handle_group_p
5206   \m_syst_helpers_handle_group_b}
5207
5208\protected\def\syst_helpers_handle_group_nop_x
5209  {\ifnum\currentgrouptype=\semisimplegroupcode
5210     \begingroup
5211     \aftergroup\endgroup
5212   \else
5213     \bgroup
5214     \aftergroup\egroup
5215   \fi
5216   \m_syst_helpers_handle_group_b}
5217
5218\protected\def\syst_helpers_handle_group_normal_x
5219  {\bgroup
5220   \afterassignment\m_syst_helpers_handle_group_normal_before_x
5221   \let\next=}
5222
5223\def\m_syst_helpers_handle_group_normal_before_x
5224  {\bgroup
5225   \m_syst_helpers_handle_group_b
5226   \bgroup
5227   \aftergroup\egroup
5228   \aftergroup\egroup}
5229
5230%D I considered it a nuisance that
5231%D
5232%D \starttyping
5233%D \color[green]
5234%D   {as grass}
5235%D \stoptyping
5236%D
5237%D was not interpreted as one would expect. This is due to the fact that \type
5238%D {\futurelet} obeys blank spaces, and a line||ending token is treated as a blank
5239%D space. So the final implementation became:
5240
5241% \protected\def\groupedcommand#1#2%
5242%   {\doifelsenextbgroup{\syst_helpers_handle_group_normal{#1}{#2}}{\syst_helpers_handle_group_nop{#1}{#2}}}
5243%
5244% \protected\def\groupedcommandcs#1#2%
5245%   {\doifelsenextbgroup{\syst_helpers_handle_group_normal{#1}{#2}}{\syst_helpers_handle_group_nop{#1}{#2}}}
5246%
5247% \protected\def\triggergroupedcommand#1%
5248%   {\doifelsenextbgroup{\syst_helpers_handle_group_normal{#1}{}}{\syst_helpers_handle_group_nop{#1}{}}}
5249%
5250% \protected\def\triggergroupedcommandcs#1%
5251%   {\doifelsenextbgroup{\syst_helpers_handle_group_normal{#1}{}}{\syst_helpers_handle_group_nop{#1}{}}}
5252%
5253% \protected\def\simplegroupedcommand#1#2%
5254%   {\doifelsenextbgroup{\syst_helpers_handle_group_simple{#1}{#2}}{\syst_helpers_handle_group_nop{#1}{#2}}}
5255%
5256% \protected\def\pickupgroupedcommand#1#2#3%
5257%   {\doifelsenextbgroup{\syst_helpers_handle_group_pickup{#1}{#2}{#3}}{\syst_helpers_handle_group_nop{#1}{#2}}}
5258
5259\protected\def\groupedcommand#1#2%
5260  {\def\m_syst_helpers_handle_group_b{#1}%
5261   \def\m_syst_helpers_handle_group_a{#2}%
5262   \doifelsenextbgroupcs\syst_helpers_handle_group_normal\syst_helpers_handle_group_nop}
5263
5264\protected\def\groupedcommandcs#1#2%
5265  {\let\m_syst_helpers_handle_group_b#1%
5266   \let\m_syst_helpers_handle_group_a#2%
5267   \doifelsenextbgroupcs\syst_helpers_handle_group_normal\syst_helpers_handle_group_nop}
5268
5269\protected\def\simplegroupedcommand#1#2%
5270  {\def\m_syst_helpers_handle_group_b{#1}%
5271   \def\m_syst_helpers_handle_group_a{#2}%
5272   \doifelsenextbgroupcs\syst_helpers_handle_group_simple\syst_helpers_handle_group_nop}
5273
5274\protected\def\pickupgroupedcommand#1#2#3%
5275  {\def\m_syst_helpers_handle_group_b{#1}%
5276   \def\m_syst_helpers_handle_group_a{#2}%
5277   \def\m_syst_helpers_handle_group_p{#3}%
5278   \doifelsenextbgroupcs\syst_helpers_handle_group_pickup\syst_helpers_handle_group_nop}
5279
5280\protected\def\triggergroupedcommand#1%
5281  {\def\m_syst_helpers_handle_group_b{#1}%
5282   \doifelsenextbgroupcs\syst_helpers_handle_group_normal_x\syst_helpers_handle_group_nop_x}
5283
5284\protected\def\triggergroupedcommandcs#1%
5285  {\let\m_syst_helpers_handle_group_b#1%
5286   \doifelsenextbgroupcs\syst_helpers_handle_group_normal_x\syst_helpers_handle_group_nop_x}
5287
5288%D Users should be aware of the fact that grouping can interfere with ones paragraph
5289%D settings that are executed after the paragraph is closed. One should therefore
5290%D explictly close the paragraph with \type {\par}, else the settings will be
5291%D forgotten and not applied. So it's:
5292%D
5293%D \starttyping
5294%D \def\BoldRaggedCenter%
5295%D   {\groupedcommand{\raggedcenter\bf}{\par}}
5296%D \stoptyping
5297
5298%D \macros
5299%D   {checkdefined}
5300%D
5301%D The bigger the system, the greater the change that user defined commands collide
5302%D with those that are part of the system. The next macro gives a warning when a
5303%D command is already defined. We considered blocking the definition, but this is
5304%D not always what we want.
5305%D
5306%D \starttyping
5307%D \checkdefined {category} {class} {command}
5308%D \stoptyping
5309%D
5310%D The user is warned with the suggestion to use \type {CAPITALS}. This suggestion
5311%D is feasible, because \CONTEXT only defines lowcased macros.
5312
5313\protected\def\showdefinederror#1#2%
5314  {\writestatus\m!system{#1 #2 replaces a macro, use CAPITALS!}}
5315
5316\protected\def\checkdefined#1#2#3%
5317  {\doifdefined{#3}{\showdefinederror{#2}{#3}}}
5318
5319%D \macros
5320%D   {GotoPar,GetPar}
5321%D
5322%D Typesetting a paragraph in a special way can be done by first grabbing the
5323%D contents of the paragraph and processing this contents grouped. The next macro
5324%D for instance typesets a paragraph in boldface.
5325%D
5326%D \starttyping
5327%D \def\remark#1\par%
5328%D   {\bgroup\bf#1\egroup}
5329%D \stoptyping
5330%D
5331%D This macro has to be called like
5332%D
5333%D \starttyping
5334%D \remark some text ... ending with \par
5335%D \stoptyping
5336%D
5337%D Instead of \type {\par} we can of course use an empty line. When we started
5338%D typesetting with \TEX, we already had produced lots of text in plain \ASCII. In
5339%D producing such simple formatted texts, we adopted an open layout, and when
5340%D switching to \TEX, we continued this open habit. Although \TEX\ permits a cramped
5341%D and badly formatted source, it adds to confusion and sometimes introduces errors.
5342%D So we prefer:
5343%D
5344%D \starttyping
5345%D \remark
5346%D
5347%D some text ... ending with an empty line
5348%D \stoptyping
5349%D
5350%D We are going to implement a mechanism that allows such open specifications. The
5351%D definition of the macro handling \type {\remark} becomes:
5352%D
5353%D \starttyping
5354%D \def\remark%
5355%D   {\BeforePar{\bgroup\bf}%
5356%D    \AfterPar{\egroup}%
5357%D    \GetPar}
5358%D \stoptyping
5359%D
5360%D A macro like \type {\GetPar} can be defined in several ways. The recent version,
5361%D the fourth one in a row, originally was far more complicated, but some
5362%D functionality has been moved to other macros.
5363%D
5364%D We start with the more simple but in some cases more appropriate alternative is
5365%D \type {\GotoPar}. This one leaves \type {\par} unchanged and is therefore more
5366%D robust. On the other hand, \type {\AfterPar} is not supported.
5367
5368\newtoks\BeforePar
5369\newtoks\AfterPar
5370
5371\protected\def\redowithpar\par
5372  {\doifelsenextchar\par\redowithpar\dodowithpar}%
5373
5374\protected\def\dowithpar#1#2%
5375  {\def\dodowithpar##1\par{#1##1#2}%
5376   \redowithpar\par}
5377
5378\protected\def\redogotopar\par
5379  {\doifelsenextchar\par\redogotopar\dodogotopar}%
5380
5381\protected\def\dogotopar#1%
5382  {\def\dodogotopar{#1}%
5383   \redogotopar\par}
5384
5385\protected\def\dogotoparcs#1%
5386  {\let\dodogotopar#1%
5387   \redogotopar\par}
5388
5389\ifdefined \ignorepars \else
5390    \protected\def\ignorepars{\dogotoparcs\relax}
5391\fi
5392
5393\protected\def\GetPar
5394  {\expanded
5395     {\dowithpar
5396        {\the\BeforePar
5397         \BeforePar\emptytoks}
5398        {\the\AfterPar
5399         \BeforePar\emptytoks
5400         \AfterPar\emptytoks}}}
5401
5402\protected\def\GotoPar
5403  {\expanded
5404     {\dogotopar
5405        {\the\BeforePar
5406         \BeforePar\emptytoks}}}
5407
5408%D \macros
5409%D   {dowithpargument,dowithwargument}
5410%D
5411%D The next macros are a variation on \type {\GetPar}. When macros expect an
5412%D argument, it interprets a grouped sequence of characters a one token. While this
5413%D adds to robustness and less ambiguous situations, we sometimes want to be a bit
5414%D more flexible, or at least want to be a bit more tolerant to user input.
5415%D
5416%D We start with a commands that acts on paragraphs. This
5417%D command is called as:
5418%D
5419%D \starttyping
5420%D \dowithpargument\command
5421%D \dowithpargument{\command ... }
5422%D \stoptyping
5423%D
5424%D In \CONTEXT\ we use this one to read in the titles of chapters, sections etc. The
5425%D commands responsible for these activities accept several alternative ways of
5426%D argument passing. In these examples, the \type {\par} can be omitted when an
5427%D empty line is present.
5428%D
5429%D \starttyping
5430%D \command{...}
5431%D \command ... \par
5432%D \command
5433%D   {...}
5434%D \command
5435%D   ... \par
5436%D \stoptyping
5437
5438\let\syst_helpers_next_par\relax
5439\let\syst_helpers_next_arg\relax
5440
5441\protected\def\dowithpargument#1%
5442  {\def\syst_helpers_next_par##1 \par{#1{##1}}%
5443   \def\syst_helpers_next_arg##1{#1{##1}}%
5444   \doifelsenextbgroup\syst_helpers_next_arg{\doifelsenextchar\par{#1{}}\syst_helpers_next_par}}
5445
5446%D The \type {p} in the previous command stands for paragraph. When we want to act
5447%D upon words we can use the \type{w} alternative.
5448%D
5449%D \starttyping
5450%D \dowithwargument\command
5451%D \dowithwargument{... \command ...}
5452%D \stoptyping
5453%D
5454%D The main difference bwteen two alternatives is in the handling of \type {\par}'s.
5455%D This time the space token acts as a delimiter.
5456%D
5457%D \starttyping
5458%D \command{...}
5459%D \command ...
5460%D \command
5461%D   {...}
5462%D \command
5463%D   ...
5464%D \stoptyping
5465
5466\let\syst_helpers_next_war\relax
5467\let\syst_helpers_next_arg\relax
5468
5469\protected\def\dowithwargument#1%
5470  {\def\syst_helpers_next_war##1 {#1{##1}}%
5471   \def\syst_helpers_next_arg##1{#1{##1}}%
5472   \doifelsenextbgroup\syst_helpers_next_arg\syst_helpers_next_war}
5473
5474%D \macros
5475%D   {dorepeat,dorepeatwithcommand}
5476%D
5477%D When doing repetitive tasks, we stromgly advice to use \type {\dorecurse}. The
5478%D next alternative however, suits better some of the \CONTEXT\ interface commands.
5479%D
5480%D \starttyping
5481%D \dorepeat[n*\command]
5482%D \stoptyping
5483%D
5484%D The value of the used \COUNTER\ can be called within
5485%D \type{\command} by \type{\repeater}.
5486%D
5487%D A slightly different alternative is:
5488%D
5489%D \starttyping
5490%D \dorepeatwithcommand[n*{...}]\command
5491%D \stoptyping
5492%D
5493%D When we call for something like:
5494%D
5495%D \starttyping
5496%D \dorepeatwithcommand[3*{Hello}]\message
5497%D \stoptyping
5498%D
5499%D we get ourselves three \type {\message{Hello}} messages in a row. In both
5500%D commands, the \type {n*} is optional. When this specification is missing, the
5501%D command executes once.
5502
5503\protected\def\dorepeatwithcommand[#1]%
5504  {\syst_helpers_repeat_with_command#1*\empty*\relax}
5505
5506\def\syst_helpers_repeat_with_command#1*#2#3*#4\relax#5%
5507  {\ifx#2\empty\syst_helpers_repeat_with_command_again[#1]#5\else\syst_helpers_repeat_with_command_indeed{#1}{#2}{#3}#5\fi}
5508
5509\def\syst_helpers_repeat_with_command_indeed#1#2#3#4%
5510  {\ifx#2\empty % redundant but gives cleaner extensions
5511     #4{#1}%
5512   \else\ifnum#1<\zerocount
5513    %\normalexpanded{\dorecurse{\number-\number#1}}{#4{-#2#3}}%
5514     \dorecurse{-#1}{#4{-#2#3}}%
5515   \else\ifx#2+%
5516     \dorecurse{#1}{#4{#3}}%
5517   \else
5518     \dorecurse{#1}{#4{#2#3}}%
5519   \fi\fi\fi}
5520
5521\def\syst_helpers_repeat_with_command_again[#1]#2%
5522  {#2{#1}}
5523
5524%D The extension hook permits something like:
5525%D
5526%D \starttyping
5527%D \bgroup
5528%D
5529%D \catcode`\*=\superscriptcatcode
5530%D
5531%D \gdef\syst_helpers_repeat_with_command_again[#1]%
5532%D   {\redodorepeatwithcommand#1*\empty*\relax}
5533%D
5534%D \gdef\redodorepeatwithcommand#1*#2#3*#4\relax#5%
5535%D   {\syst_helpers_repeat_with_command_indeed{#1}{#2}{#3}#5}
5536%D
5537%D \egroup
5538%D \stoptyping
5539%D
5540%D although one may wonder if changing the catcode of \type {*} is wise.
5541
5542%D \macros
5543%D   {doifstringinstringelse}
5544%D
5545%D The next macro is meant for situations where both strings are macros. This save
5546%D some unneeded expansion.
5547%D
5548%D \starttyping
5549%D \def\doifstringinstringelse#1#2%
5550%D   {\syst_helpers_do_if_in_string_else#1#2%
5551%D      \expandafter\firstoftwoarguments
5552%D    \else
5553%D      \expandafter\secondoftwoarguments
5554%D    \fi}
5555%D \stoptyping
5556%D
5557%D A bit faster is:
5558
5559\def\syst_helpers_if_instring_else_indeed#1%
5560  {\if#1@%
5561     \expandafter\secondoftwoarguments
5562   \else
5563     \expandafter\firstoftwoarguments
5564   \fi}
5565
5566\def\doifelsestringinstring#1#2%
5567  {\expandafter\def\expandafter\syst_helpers_if_instring_else\expandafter##\expandafter1#1##2##3^^^^0004%
5568     {\syst_helpers_if_instring_else_indeed##2}%
5569   \expandafter\expandafter\expandafter\syst_helpers_if_instring_else\expandafter#2#1@@^^^^0004}
5570
5571\let\doifstringinstringelse\doifelsestringinstring
5572
5573%D \macros
5574%D   {appendtoks,prependtoks,appendtoksonce,prependtoksonce,
5575%D    doifintokselse,flushtoks,dotoks}
5576%D
5577%D We use tokenlists sparsely within \CONTEXT, because the comma separated lists are
5578%D more suitable for the user interface. Nevertheless we have:
5579%D
5580%D \starttyping
5581%D (\doglobal) \appendtoks ... \to\tokenlist
5582%D (\doglobal) \prependtoks ... \to\tokenlist
5583%D (\doglobal) \flushtoks\tokenlist
5584%D             \dotoks\tokenlist
5585%D \stoptyping
5586%D
5587%D These macros are clones of the ones implemented in page~378 of Knuth's \TEX book.
5588
5589\newtoks\t_syst_helpers_scratch
5590\let    \m_syst_helpers_scratch\empty
5591
5592% no longer \def but \let to target toks .. the space gobbling \relax will go
5593
5594% \protected\def\appendtoks     {\syst_helpers_append_toks      \relax}
5595% \protected\def\prependtoks    {\syst_helpers_prepend_toks     \relax}
5596% \protected\def\appendtoksonce {\syst_helpers_append_toks_once \relax}
5597% \protected\def\prependtoksonce{\syst_helpers_prepend_toks_once\relax}
5598%
5599% \def\syst_helpers_append_toks_indeed
5600%   {\dodoglobal\m_syst_helpers_scratch\doubleexpandafter{\expandafter\the\expandafter\m_syst_helpers_scratch\the\t_syst_helpers_scratch}}
5601%
5602% \def\syst_helpers_prepend_toks_indeed
5603%   {\dodoglobal\m_syst_helpers_scratch\doubleexpandafter{\expandafter\the\expandafter\t_syst_helpers_scratch\the\m_syst_helpers_scratch}}
5604%
5605% \def\syst_helpers_append_toks#1\to#2%
5606%   {\let\m_syst_helpers_scratch#2%
5607%    \t_syst_helpers_scratch\expandafter{\gobbleoneargument#1}%
5608%    \syst_helpers_append_toks_indeed}
5609%
5610% \def\syst_helpers_prepend_toks#1\to#2%
5611%   {\let\m_syst_helpers_scratch#2%
5612%    \t_syst_helpers_scratch\expandafter{\gobbleoneargument#1}%
5613%    \syst_helpers_prepend_toks_indeed}
5614%
5615% \def\syst_helpers_append_toks_once#1\to#2%
5616%   {\let\m_syst_helpers_scratch#2%
5617%    \t_syst_helpers_scratch\expandafter{\gobbleoneargument#1}%
5618%    \doifelseintoks\t_syst_helpers_scratch\m_syst_helpers_scratch
5619%      \donothing
5620%      \syst_helpers_append_toks_indeed}
5621%
5622% \def\syst_helpers_prepend_toks_once#1\to#2%
5623%   {\let\m_syst_helpers_scratch#2%
5624%    \t_syst_helpers_scratch\expandafter{\gobbleoneargument#1}%
5625%    \doifelseintoks\t_syst_helpers_scratch\m_syst_helpers_scratch
5626%      \donothing
5627%      \syst_helpers_prepend_toks_indeed}
5628
5629% \protected\def\appendtoks#1\to#2%
5630%   {\toksapp#2{#1}%
5631%    \ifx\dodoglobal\relax\else
5632%      \dodoglobal#2#2%
5633%    \fi}
5634%
5635% \protected\def\prependtoks#1\to#2%
5636%   {\tokspre#2{#1}%
5637%    \ifx\dodoglobal\relax\else
5638%       \dodoglobal#2#2%
5639%    \fi}
5640%
5641% \def\syst_helpers_append_toks_indeed
5642%   {\toksapp\m_syst_helpers_scratch\t_syst_helpers_scratch
5643%    \ifx\dodoglobal\relax\else
5644%      \dodoglobal\m_syst_helpers_scratch\m_syst_helpers_scratch
5645%    \fi}
5646%
5647% \def\syst_helpers_prepend_toks_indeed
5648%   {\tokspre\m_syst_helpers_scratch\t_syst_helpers_scratch
5649%    \ifx\dodoglobal\relax\else
5650%      \dodoglobal\m_syst_helpers_scratch\m_syst_helpers_scratch
5651%    \fi}
5652
5653\protected\def\appendtoks#1\to#2%
5654  {\ifx\dodoglobal\relax
5655     \expandafter\toksapp
5656   \else
5657     \resetglobal
5658     \expandafter\gtoksapp
5659   \fi#2{#1}}
5660
5661\protected\def\prependtoks#1\to#2%
5662  {\ifx\dodoglobal\relax
5663     \expandafter\tokspre
5664   \else
5665     \resetglobal
5666     \expandafter\gtokspre
5667   \fi#2{#1}}
5668
5669\def\syst_helpers_append_toks_indeed
5670  {\ifx\dodoglobal\relax
5671     \expandafter\toksapp
5672   \else
5673     \resetglobal
5674     \expandafter\gtoksapp
5675   \fi\m_syst_helpers_scratch\t_syst_helpers_scratch}
5676
5677\def\syst_helpers_prepend_toks_indeed
5678  {\ifx\dodoglobal\relax
5679     \expandafter\tokspre
5680   \else
5681     \resetglobal
5682     \expandafter\gtokspre
5683   \fi\m_syst_helpers_scratch\t_syst_helpers_scratch}
5684
5685\protected\def\appendtoksonce#1\to#2%
5686  {\let\m_syst_helpers_scratch#2%
5687   \t_syst_helpers_scratch{#1}%
5688   \doifelseintoks\t_syst_helpers_scratch\m_syst_helpers_scratch
5689     \donothing
5690     \syst_helpers_append_toks_indeed}
5691
5692\protected\def\prependtoksonce#1\to#2%
5693  {\let\m_syst_helpers_scratch#2%
5694   \t_syst_helpers_scratch{#1}%
5695   \doifelseintoks\t_syst_helpers_scratch\m_syst_helpers_scratch
5696     \donothing
5697     \syst_helpers_prepend_toks_indeed}
5698
5699%D The test macro:
5700
5701\protected\def\doifelseintoks#1#2% #1 en #2 zijn toks
5702  {\edef\asciia{\detokenize\expandafter{\the#1}}%
5703   \edef\asciib{\detokenize\expandafter{\the#2}}%
5704   \doifelsestringinstring\asciia\asciib}
5705
5706\let\doifintokselse\doifelseintoks
5707
5708%D Moved from \type {lxml-ini.tex} to here. This one is for generators that collect
5709%D stuff piecewise, which is sometimes hard on mechanisms that grab content using
5710%D delimiters:
5711%D
5712%D \starttyping
5713%D \startcollecting
5714%D \startcollect \bTABLE \stopcollect
5715%D     \startcollect \bTR \stopcollect
5716%D         \startcollect \bTD \stopcollect
5717%D         \startcollect   foo\stopcollect
5718%D         \startcollect \eTD \stopcollect
5719%D         \startcollect \bTD \stopcollect
5720%D         \startcollect   bar\stopcollect
5721%D         \startcollect \eTD \stopcollect
5722%D     \startcollect \eTR \stopcollect
5723%D \startcollect \eTABLE \stopcollect
5724%D \stopcollecting
5725%D \stoptyping
5726
5727\newtoks \collectingtoks
5728
5729\protected\def\startcollect        #1\stopcollect        {\toksapp \collectingtoks{#1}}
5730\protected\def\startexpandedcollect#1\stopexpandedcollect{\etoksapp\collectingtoks{#1}}
5731
5732\protected\def\startcollecting{\collectingtoks\emptytoks}
5733\protected\def\stopcollecting {\the\collectingtoks}
5734
5735\protected\def\collect        {\toksapp \collectingtoks}
5736\protected\def\collectexpanded{\etoksapp\collectingtoks}
5737
5738%D A nice one too:
5739
5740% {\scratchtoks{abc} \removetoks b\from\scratchtoks [\the\scratchtoks]}
5741% {\scratchtoks{abc} \removetoks x\from\scratchtoks [\the\scratchtoks]}
5742% {\scratchtoks{} \removetoks x\from\scratchtoks [\the\scratchtoks]}
5743% {\scratchtoks{xaa} \removetoks x\from\scratchtoks [\the\scratchtoks]}
5744% {\scratchtoks{a\relax b} \removetoks \relax\from\scratchtoks [\showthe\scratchtoks]}
5745
5746\protected\def\removetoks#1\from#2%
5747  {\def\syst_helpers_remove_toks##1#1##2\empty\empty\empty##3^^^^0004%
5748     {\def\m_syst_string_one{##3}%
5749      \ifx\m_syst_string_one\empty#2{##1}\else#2{##1##2}\fi}%
5750   \expandafter\syst_helpers_remove_toks\the#2\empty\empty\empty#1\empty\empty\empty^^^^0004}
5751
5752%D Also:
5753
5754% \protected\def\appendetoks #1\to{\normalexpanded{\appendtoks #1}\to}
5755% \protected\def\prependetoks#1\to{\normalexpanded{\prependtoks#1}\to}
5756
5757% \protected\def\appendetoks#1\to#2%
5758%   {\etoksapp#2{#1}%
5759%    \ifx\dodoglobal\relax\else
5760%      \global#2#2%
5761%    \fi}
5762%
5763% \protected\def\prependetoks#1\to#2%
5764%   {\etokspre#2{#1}%
5765%    \ifx\dodoglobal\relax\else
5766%       \global#2#2%
5767%    \fi}
5768
5769\protected\def\appendetoks#1\to#2%
5770  {\ifx\dodoglobal\relax
5771     \expandafter\etoksapp
5772   \else
5773     \resetglobal
5774     \expandafter\xtoksapp
5775   \fi#2{#1}}
5776
5777\protected\def\prependetoks#1\to#2%
5778  {\ifx\dodoglobal\relax
5779     \expandafter\etokspre
5780   \else
5781     \resetglobal
5782     \expandafter\xtokspre
5783   \fi#2{#1}}
5784
5785%D Hm.
5786
5787\protected\def\flushtoks#1% nb: can reassign to #1 again, hence the indirectness
5788  {\t_syst_helpers_scratch#1\relax
5789   \dodoglobal#1\emptytoks
5790   \the\t_syst_helpers_scratch\relax}
5791
5792% better: \def\flushtoks#1{\normalexpanded{\noexpand\dodoglobal#1\emptytoks\the#\relax}}
5793
5794\let\dotoks\the
5795
5796%D \macros
5797%D   {beforesplitstring,aftersplitstring}
5798%D
5799%D These both commands split a string at a given point in two
5800%D parts, so \type{x.y} becomes \type{x} or \type{y}.
5801%D
5802%D \starttyping
5803%D \beforesplitstring test.tex\at.\to\filename
5804%D \aftersplitstring  test.tex\at.\to\extension
5805%D \stoptyping
5806%D
5807%D The first routine looks (and is indeed) a bit simpler than the second one. The
5808%D alternative looking more or less like the first one did not always give the
5809%D results we needed. Both implementations show some insight in the manipulation of
5810%D arguments.
5811
5812\let\syst_helpers_split_string\relax
5813
5814\protected\def\beforesplitstring#1\at#2\to#3%
5815  {\def\syst_helpers_split_string##1#2##2#2##3\\%
5816     {\def#3{##1}}%
5817   \expandafter\syst_helpers_split_string#1#2#2\\}
5818
5819\protected\def\aftersplitstring#1\at#2\to#3%
5820  {\def\syst_helpers_split_string##1#2##2@@@##3\\%
5821     {\def#3{##2}}%
5822   \expandafter\syst_helpers_split_string#1@@@#2@@@\\}
5823
5824%D \macros
5825%D   {splitstring,greedysplitstring}
5826%D
5827%D A bonus macro.
5828
5829\protected\def\splitstring#1\at#2\to#3\and#4%
5830  {\def\syst_helpers_split_string##1#2##2\empty\empty\empty##3\\%
5831     {\def#3{##1}%
5832      \def\syst_helpers_split_string{##3}%
5833      \ifx\syst_helpers_split_string\empty
5834        \let#4\empty
5835      \else
5836        \def#4{##2}%
5837      \fi}%
5838   \expandafter\syst_helpers_split_string#1\empty\empty\empty#2\empty\empty\empty\\}
5839
5840\protected\def\greedysplitstring#1\at#2\to#3\and#4%
5841  {\edef\asciib{#1}%
5842   \let\asciic\asciib
5843   \let#3\empty
5844   \let#4\empty
5845   \doloop
5846     {\expandafter\splitstring\asciib\at#2\to\asciia\and\asciib
5847      \ifx\asciib\empty
5848        \exitloop
5849      \else
5850        % not \edef#3{\ifx#3\empty\else#3#2\fi\asciia} else
5851        % /root/path fails because then #3==empty
5852        \edef#3{\ifcase\recurselevel\or\else#3#2\fi\asciia}%
5853        \let#4\asciib
5854      \fi}%
5855  \ifx#3\empty\let#3\asciic\fi}
5856
5857%D \macros
5858%D   {beforetestandsplitstring,
5859%D    aftertestandsplitstring,
5860%D    testandsplitstring}
5861
5862\protected\def\beforetestandsplitstring#1\at#2\to#3%
5863  {\def\syst_helpers_split_string##1#2##2#2##3##4\\%
5864     {\ifx##3\empty\let#3\empty\else\def#3{##1}\fi}%
5865   \expandafter\syst_helpers_split_string#1#2#2\empty\\}
5866
5867\protected\def\aftertestandsplitstring#1\at#2\to#3%
5868  {\def\syst_helpers_split_string ##1#2##2@@@##3##4\\%
5869     {\ifx##3\empty\let#3\empty\else\def#3{##2}\fi}%
5870   \expandafter\syst_helpers_split_string #1@@@#2@@@\empty\\}
5871
5872\def\testandsplitstring#1\at#2\to#3\and#4%
5873  {\def\syst_helpers_split_string##1#2##2#2##3##4\\%
5874     {\ifx##3\empty\let#3\empty\let#4\empty\else\def#3{##1}\def#4{##2}\fi}%
5875   \expandafter\syst_helpers_split_string#1#2#2\empty\\}
5876
5877%D \macros
5878%D   {splitatperiod,
5879%D   {splitatcomma,
5880%D    splitatasterisk,
5881%D    splitatcolon,
5882%D    splitatcolons}
5883
5884\protected\def\splitatperiod  #1{\normalexpanded{\syst_helpers_splitatperiod  #1}..\relax}
5885\protected\def\splitatcomma   #1{\normalexpanded{\syst_helpers_splitatcomma   #1},,\relax}    % not at ", "
5886\protected\def\splitatasterisk#1{\normalexpanded{\syst_helpers_splitatasterisk#1}**\relax}
5887\protected\def\splitatcolon   #1{\normalexpanded{\syst_helpers_splitatcolon   #1}::\relax}
5888\protected\def\splitatcolons  #1{\normalexpanded{\syst_helpers_splitatcolons  #1}::::\relax}
5889
5890\protected\def\syst_helpers_splitatperiod    #1.#2.#3\relax#4#5{\def#4{#1}\def#5{#2}}
5891\protected\def\syst_helpers_splitatcomma     #1,#2,#3\relax#4#5{\def#4{#1}\def#5{#2}}
5892\protected\def\syst_helpers_splitatasterisk  #1*#2*#3\relax#4#5{\def#4{#1}\def#5{#2}}
5893\protected\def\syst_helpers_splitatcolon     #1:#2:#3\relax#4#5{\def#4{#1}\def#5{#2}}
5894\protected\def\syst_helpers_splitatcolons  #1::#2::#3\relax#4#5{\edef#4{#1}\edef#5{#2}}
5895
5896%D \macros
5897%D   {removesubstring}
5898%D
5899%D A first application of the two routines defined above is:
5900%D
5901%D \starttyping
5902%D \removesubstring-\from first-last\to\nothyphenated
5903%D \stoptyping
5904%D
5905%D Which in terms of \TEX\ looks like:
5906
5907\protected\def\removesubstring#1\from#2\to#3%
5908  {\splitstring#2\to\m_syst_string_one\and\m_syst_string_two
5909   \dodoglobal#3{\m_syst_string_one\m_syst_string_two}}
5910
5911%D \macros
5912%D   {appendtocommalist,prependtocommalist,
5913%D    addtocommalist,removefromcommalist}
5914%D
5915%D When working with comma separated lists, one sooner or later want the tools to
5916%D append or remove items from such a list. When we add an item, we first check if
5917%D it's already there. This means that every item in the list is unique.
5918%D
5919%D \starttyping
5920%D \addtocommalist      {alfa}  \name
5921%D \addtocommalist      {beta}  \name
5922%D \addtocommalist      {gamma} \name
5923%D \removefromcommalist {beta}  \name
5924%D \stoptyping
5925%D
5926%D These commands can be prefixed with \type {\doglobal}. The implementation of the
5927%D second command is more complecated, because we have to take leading spaces into
5928%D account. Keep in mind that users may provide lists with spaces after the commas.
5929%D When one item is left, we also have to get rid of trailing spaces.
5930%D
5931%D \starttyping
5932%D \def\words{alfa, beta, gamma, delta}
5933%D \def\words{alfa,beta,gamma,delta}
5934%D \stoptyping
5935%D
5936%D Removing an item takes more time than adding one. A fast appending alternative,
5937%D without any testing, is also provided:
5938%D
5939%D \starttyping
5940%D \appendtocommalist  {something} \name
5941%D \prependtocommalist {something} \name
5942%D \stoptyping
5943%D
5944%D This can be implemented as follows:
5945%D
5946%D \starttyping
5947%D \def\appendtocommalist#1#2%
5948%D   {\ifx#2\empty
5949%D      \dodoglobal\edef#2{#1}%
5950%D    \else % no test on empty
5951%D      \dodoglobal\edef#2{#2,#1}%
5952%D    \fi}
5953%D
5954%D \def\prependtocommalist#1#2%
5955%D   {\ifx#2\empty
5956%D      \dodoglobal\edef#2{#1}%
5957%D    \else % no test on empty
5958%D      \dodoglobal\edef#2{#1,#2}%
5959%D    \fi}
5960%D \stoptyping
5961%D
5962%D The faster alternatives are:
5963
5964\protected\def\appendtocommalist#1#2%
5965  {\dodoglobal\edef#2{\ifx#2\empty\else#2,\fi#1}}
5966
5967\protected\def\prependtocommalist#1#2%
5968  {\dodoglobal\edef#2{#1\ifx#2\empty\else,#2\fi}}
5969
5970\protected\def\addtocommalist#1#2% {item} \cs
5971  {\rawdoifelseinset{#1}#2\resetglobal
5972     {\dodoglobal\edef#2{\ifx#2\empty\else#2,\fi#1}}}
5973
5974\protected\def\pretocommalist#1#2% {item} \cs
5975  {\rawdoifelseinset{#1}#2\resetglobal
5976     {\dodoglobal\edef#2{#1\ifx#2\empty\else,#2\fi}}}
5977
5978\protected\def\robustdoifelseinset#1#2%
5979  {\edef\m_syst_string_one{\detokenize\expandafter{\normalexpanded{#1}}}%
5980   \edef\m_syst_string_two{\detokenize\expandafter{\normalexpanded{#2}}}%
5981   \rawdoifelseinset\m_syst_string_one\m_syst_string_two}
5982
5983\let\robustdoifinsetelse\robustdoifelseinset
5984
5985\protected\def\robustaddtocommalist#1#2% {item} \cs
5986  {\robustdoifelseinset{#1}#2\resetglobal
5987     {\dodoglobal\edef#2{\ifx#2\empty\else#2,\fi#1}}}
5988
5989\protected\def\robustpretocommalist#1#2% {item} \cs
5990  {\robustdoifelseinset{#1}#2\resetglobal
5991     {\dodoglobal\edef#2{#1\ifx#2\empty\else,#2\fi}}}
5992
5993\protected\def\xsplitstring#1#2% \cs {str}
5994  {\def\syst_helpers_split_string##1,#2,##2,#2,##3\\%
5995     {\edef\m_syst_string_one{\bcleanedupcommalist##1\empty\empty\relax}%
5996      \edef\m_syst_string_two{\acleanedupcommalist##2,,\relax}}%
5997   \expandafter\syst_helpers_split_string\expandafter,#1,,#2,,#2,\\}
5998
5999\def\bcleanedupcommalist#1#2#3\relax{\if#1,\else#1\fi\if#2,\else#2\fi#3}
6000\def\bcleanedupcommalist#1#2\relax{\if#1,\else#1\fi#2}
6001\def\acleanedupcommalist#1,,#2\relax{#1}
6002
6003\protected\def\removefromcommalist#1#2% to be sped up
6004  {\rawdoifelseinset{#1}#2%
6005     {\normalexpanded{\xsplitstring\noexpand#2{#1}}%
6006      \dodoglobal\edef#2%
6007        {\ifx\m_syst_string_one\empty
6008           \m_syst_string_two
6009         \else
6010           \m_syst_string_one\ifx\m_syst_string_two\empty\else,\m_syst_string_two\fi
6011         \fi}}
6012     \resetglobal}
6013
6014% \protected\def\addtocommalist#1#2% upto 3 times slower
6015%   {\dodoglobal\edef#2{\ctxcommand{addtocommalist(\!!bs#1\!!es,\!!bs#2\!!es)}}}
6016%
6017% \protected\def\removefromcommalist#1#2% faster and more robust
6018%   {\dodoglobal\edef#2{\ctxcommand{addtocommalist(\!!bs#1\!!es,\!!bs#2\!!es)}}}
6019
6020%D \macros
6021%D   {substituteincommalist}
6022%D
6023%D Slow but seldom used, so for the moment we stick to this implementation.
6024%D
6025%D \starttyping
6026%D \substituteincommalist{old}{new}{list}
6027%D \stoptyping
6028
6029\def\syst_helpers_substitute_in_comma_list_step#1%
6030  {\edef\m_syst_string_three{#1}%
6031   \ifx\m_syst_string_one\m_syst_string_three
6032     \ifx\m_syst_string_two\empty \else
6033       \edef\m_syst_string_four{\ifx\m_syst_string_four\empty\else\m_syst_string_four,\fi\m_syst_string_two}%
6034     \fi
6035   \else
6036     \edef\m_syst_string_four{\ifx\m_syst_string_four\empty\else\m_syst_string_four,\fi#1}%
6037   \fi}
6038
6039\protected\def\substituteincommalist#1#2#3% old, new, list (slooow)
6040  {\edef\m_syst_string_one{#1}%
6041   \edef\m_syst_string_two{#2}%
6042   \let\m_syst_string_four\empty
6043   \normalexpanded{\rawprocesscommacommand[#3]}\syst_helpers_substitute_in_comma_list_step
6044   \let#3\m_syst_string_four}
6045
6046%D \macros
6047%D   {replaceincommalist}
6048%D
6049%D The next macro can be used to replace an indexed element in a commalist:
6050%D
6051%D \starttyping
6052%D \replaceincommalist\MyList{2}
6053%D \stoptyping
6054%D
6055%D Element~2 will be replaced by the current meaning of the macro \type
6056%D {\newcommalistelement}. The old meaning is saved in \type {\commalistelement}.
6057%D The replacement honors grouped items, like in:
6058%D
6059%D \starttyping
6060%D \def\MyList{a,b,c,d,e,f}   \replaceincommalist\MyList{3}
6061%D \def\MyList{a,b,c,d,e,f}   \replaceincommalist\MyList{3}
6062%D \def\MyList{a,{b,c},d,e,f} \replaceincommalist\MyList{3}
6063%D \def\MyList{a,b,c,{d,e,f}} \replaceincommalist\MyList{3}
6064%D \stoptyping
6065%D
6066%D This macro was used in the bibtex code (and is probably no longer needed).
6067
6068\newcount\c_syst_helpers_comma_list_index
6069\let     \m_syst_helpers_comma_list_target\empty
6070
6071\let\newcommalistelement\empty
6072
6073\def\syst_helpers_replace_in_comma_list_step#1%
6074  {\ifnum\commalistcounter=\c_syst_helpers_comma_list_index\relax
6075     \ifx\newcommalistelement\empty\else
6076       \ifx\m_syst_helpers_comma_list_target\empty
6077         \let\m_syst_helpers_comma_list_target\newcommalistelement
6078       \else
6079         \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter
6080           \m_syst_helpers_comma_list_target\expandafter\expandafter\expandafter
6081             {\expandafter\m_syst_helpers_comma_list_target\expandafter,\newcommalistelement}%
6082       \fi
6083     \fi
6084     \def\commalistelement{#1}%
6085   \else
6086     \ifx\m_syst_helpers_comma_list_target\empty
6087       \ifx\nexttoken\bgroup % is known -)
6088         \def\m_syst_helpers_comma_list_target{{#1}}%
6089       \else
6090         \def\m_syst_helpers_comma_list_target{#1}%
6091       \fi
6092     \else
6093       \ifx\nexttoken\bgroup % is known -)
6094         \expandafter\def\expandafter\m_syst_helpers_comma_list_target\expandafter{\m_syst_helpers_comma_list_target,{#1}}%
6095       \else
6096         \expandafter\def\expandafter\m_syst_helpers_comma_list_target\expandafter{\m_syst_helpers_comma_list_target,#1}%
6097       \fi
6098     \fi
6099   \fi
6100   \advance\commalistcounter\plusone}
6101
6102\protected\def\replaceincommalist#1#2% #1 = commalistelement #2 = position starts at 1
6103  {\c_syst_helpers_comma_list_index#2\relax
6104   \let\m_syst_helpers_comma_list_target\empty
6105   \let\commalistelement\empty
6106   \commalistcounter\plusone
6107   \expandafter\processcommalist\expandafter[#1]\syst_helpers_replace_in_comma_list_step
6108   \dodoglobal\let#1\m_syst_helpers_comma_list_target}
6109
6110%D \macros
6111%D   {globalprocesscommalist}
6112%D
6113%D The commalist processing commands are characterized by the fact that the way they
6114%D handle expansion as well as the fact that they can be nested. This makes them
6115%D kind of useless for handling comma lists in alignments. In these situations the
6116%D next macro can be of use.
6117
6118\let\m_syst_helpers_comma_list_command_global\empty
6119
6120\def\syst_helpers_comma_list_command_global_step#1,%
6121  {\if]#1\else
6122     \m_syst_helpers_comma_list_command_global{#1}%
6123     \expandafter\syst_helpers_comma_list_command_global_step
6124   \fi}
6125
6126\protected\def\globalprocesscommalist[#1]#2%
6127  {\glet\m_syst_helpers_comma_list_command_global#2%
6128   \expandafter\syst_helpers_comma_list_command_global_step#1,],}
6129
6130%D \macros
6131%D   {withoutpt,PtToCm,
6132%D    numberofpoints,dimensiontocount}
6133%D
6134%D We can convert point into centimeters with:
6135%D
6136%D \starttyping
6137%D \PtToCm{dimension}
6138%D \stoptyping
6139
6140{\catcode`\.=\othercatcode
6141 \catcode`\p=\othercatcode
6142 \catcode`\t=\othercatcode
6143 \gdef\WITHOUTPT#1pt{#1}}
6144
6145\def\withoutpt#1%
6146  {\expandafter\WITHOUTPT#1}
6147
6148%D The capitals are needed because \type {p} and \type {t} have catcode~12, while
6149%D macronames only permit tokens with the catcode~11. As a result we cannot use the
6150%D \type {.group} primitives. Those who want to know more about this kind of
6151%D manipulations, we advice to study the \TEX book in detail. Because this macro
6152%D does not do any assignment, we can use it in the following way too.
6153
6154\def\PtToCm#1%
6155  {\withoutpt\the\dimexpr0.0351459804\dimexpr#1\relax\relax cm}
6156
6157%D We also support:
6158%D
6159%D \starttyping
6160%D \numberofpoints   {dimension}
6161%D \dimensiontocount {dimension} {\count}
6162%D \stoptyping
6163%D
6164%D Both macros return a rounded number.
6165
6166% \dimensiontocount{10.49pt}\scratchcounter \the\scratchcounter / \numberofpoints{10.49pt}
6167% \dimensiontocount{10.51pt}\scratchcounter \the\scratchcounter / \numberofpoints{10.51pt}
6168
6169\def\dimensiontocount#1#2{#2\numexpr\dimexpr#1\relax/\maxcard\relax}
6170