syst-aux.mkiv /size: 270 Kb    last modification: 2021-10-28 13:50
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{\csname#1\endcsname} % maybe: \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{\csname#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%D The next alternative proved to be upto twice as fast on tasks like checking
1479%D reserved words in pretty verbatim typesetting! This is mainly due to the fact
1480%D that passing (expanded) strings is much slower that passing a macro.
1481%D
1482%D \starttyping
1483%D \doifincsnameelse {substring} {\string} {then ...} {else ...}
1484%D \stoptyping
1485%D
1486%D Where \type {\doifinstringelse} does as much expansion as possible, the latter
1487%D alternative does minimal (one level) expansion.
1488
1489\protected\def\syst_helpers_do_if_in_csname_else#1#2%
1490  {\def\syst_helpers_do_do_if_in_csname_else##1#1##2##3^^^^0004%
1491     {\unless\if##2@}%
1492   \expandafter\syst_helpers_do_do_if_in_csname_else#2#1@@^^^^0004}
1493
1494\protected\def\doifelseincsname#1#2%
1495  {\normalexpanded{\syst_helpers_do_if_in_csname_else{#1}}{#2}%
1496     \expandafter\firstoftwoarguments
1497   \else
1498     \expandafter\secondoftwoarguments
1499   \fi}
1500
1501\let\doifincsnameelse\doifelseincsname
1502
1503%D \macros
1504%D   {doifnumberelse,doifnumber,doifnotnumber}
1505%D
1506%D The next macro executes a command depending of the outcome of a test on numerals.
1507%D This is probably one of the fastest test possible, exept from a less robust
1508%D 10||step \type {\if}||ladder or some tricky \type {\lcode} checking.
1509%D
1510%D \starttyping
1511%D \doifnumberelse {string} {then ...} {else ...}
1512%D \stoptyping
1513%D
1514%D The macro accepts \type {123}, \type {abc}, \type {{}}, \type {\getal} and \type
1515%D {\the\count...}. This macro is a rather dirty one.
1516
1517\def\doifelsenumber#1% does not accept counters (fully expandable)
1518  {\ifcase0\ifcase1#1\or\or\or\or\or\or\or\or\or\else1\fi\space
1519     \expandafter\secondoftwoarguments
1520   \else
1521     \expandafter\firstoftwoarguments
1522   \fi}
1523
1524\def\doifnumber#1%
1525  {\ifcase0\ifcase1#1\or\or\or\or\or\or\or\or\or\else1\fi\space
1526     \expandafter\firstofoneargument
1527   \else
1528     \expandafter\gobbleoneargument
1529   \fi}
1530
1531\def\doifnotnumber#1%
1532  {\ifcase0\ifcase1#1\or\or\or\or\or\or\or\or\or\else1\fi\space
1533     \expandafter\gobbleoneargument
1534   \else
1535     \expandafter\firstofoneargument
1536   \fi}
1537
1538\let\doifnumberelse\doifelsenumber
1539
1540%D \macros
1541%D   {setpercentdimen}
1542%D
1543%D \starttyping
1544%D \scratchdimen=100pt \setpercentdimen\scratchdimen{10\letterpercent}
1545%D \scratchdimen=100pt \setpercentdimen\scratchdimen{5pt}
1546%D \scratchdimen       \percentdimen   \hsize       {10\letterpercent}
1547%D \stoptyping
1548
1549\def\percentdimen#1#2% dimen percentage (with %)
1550  {\dimexpr\clf_percentageof{#2}\dimexpr#1\relax}
1551
1552\protected\def\setpercentdimen#1#2% dimen percentage (with %)
1553  {#1=\clf_percentageof{#2}\dimexpr#1\relax}
1554
1555%D \macros
1556%D   {makerawcommalist,
1557%D    rawdoinsetelse,
1558%D    rawprocesscommalist,
1559%D    rawprocessaction}
1560%D
1561%D Some of the commands mentioned earlier are effective but slow. When one is
1562%D desperately in need of faster alternatives and when the conditions are
1563%D predictable safe, the \type {\raw} alternatives come into focus. A major drawback
1564%D is that they do not take \type {\c!constants} into account, simply because no
1565%D expansion is done. This is no problem with \type {\rawprocesscommalist}, because
1566%D this macro does not compare anything. Expandable macros are permitted as search
1567%D string.
1568%D
1569%D \starttyping
1570%D \makerawcommalist[string,string,...]\stringlist
1571%D \rawdoifelseinset{string}{string,...}{...}{...}
1572%D \rawprocesscommalist[string,string,...]\commando
1573%D \rawprocessaction[x][a=>\a,b=>\b,c=>\c]
1574%D \stoptyping
1575%D
1576%D Spaces embedded in the list, for instance after commas, spoil the search process.
1577%D The gain in speed depends on the length of the argument (the longer the argument,
1578%D the less we gain).
1579
1580\protected\def\makerawcommalist[#1]#2% use \processnext ... here
1581  {\def\syst_helpers_do_make_raw_comma_list##1% we don't expand ##1
1582     {\ifx#2\empty
1583        \def#2{##1}%
1584      \else
1585        \expandafter\def\expandafter#2\expandafter{#2,##1}%
1586      \fi}%
1587   \let#2\empty
1588   \processcommalist[#1]\syst_helpers_do_make_raw_comma_list}
1589
1590\def\syst_helpers_raw_process_comma_item#1,#2% #2 eats up preceding space
1591  {\if]#1\else
1592     \csname\??nextcommalevel\the\commalevel\endcsname{#1}%
1593     \expandafter\syst_helpers_raw_process_comma_item
1594   \fi#2}
1595
1596\protected\def\rawprocesscommalist[#1]#2% accepteert ook [\cs]
1597  {\global\advance\commalevel \plusone
1598   \expandafter\let\csname\??nextcommalevel\the\commalevel\endcsname#2%
1599   \expandafter\syst_helpers_raw_process_comma_item#1,],% \relax
1600   \global\advance\commalevel \minusone }
1601
1602\protected\def\rawprocesscommacommand[#1]% not really needed
1603  {\normalexpanded{\rawprocesscommalist[#1]}}
1604
1605%D Here is one without nesting:
1606
1607\protected\def\syst_helpers_fast_process_comma_item#1,#2% #2 eats up preceding space
1608  {\if]#1\else
1609     \syst_helpers_fast_process_comma_command{#1}%
1610     \expandafter\syst_helpers_fast_process_comma_item
1611   \fi#2}
1612
1613\protected\def\fastprocesscommalist[#1]#2% accepteert ook [\cs]
1614  {\let\syst_helpers_fast_process_comma_command#2%
1615   \expandafter\syst_helpers_fast_process_comma_item#1,],}% \relax
1616
1617\protected\def\fastprocesscommacommand[#1]#2% accepteert ook [\cs]
1618  {\let\syst_helpers_fast_process_comma_command#2%
1619   \normalexpanded{\syst_helpers_fast_process_comma_item#1},],}% \relax
1620
1621% \def\rawdoifelseinset#1#2{\doifinstringelse{,#1,}{,#2,}}
1622% \def\rawdoifinset    #1#2{\doifinstring    {,#1,}{,#2,}}
1623
1624\def\m_syst_two_commas{,,}
1625
1626\protected\def\rawdoifelseinset#1%
1627  {\edef\m_syst_sub_string{,#1,}% expand #1 here
1628   \ifx\m_syst_sub_string\m_syst_two_commas
1629     \expandafter\thirdofthreearguments
1630   \else
1631     \expandafter\syst_helpers_raw_do_if_in_set_else
1632   \fi}
1633
1634\let\rawdoifinsetelse\rawdoifelseinset
1635
1636\protected\def\syst_helpers_raw_do_if_in_set_else#1%
1637  {\syst_helpers_do_if_in_string_else\m_syst_sub_string{,#1,}%
1638     \expandafter\firstoftwoarguments
1639   \else
1640     \expandafter\secondoftwoarguments
1641   \fi}
1642
1643\protected\def\rawdoifinset#1%
1644  {\edef\m_syst_sub_string{,#1,}% expand #1 here
1645   \ifx\m_syst_sub_string\m_syst_two_commas
1646     \expandafter\gobbletwoarguments
1647   \else
1648     \expandafter\syst_helpers_raw_do_if_in_set
1649   \fi}
1650
1651\protected\def\syst_helpers_raw_do_if_in_set#1%%
1652  {\syst_helpers_do_if_in_string_else\m_syst_sub_string{,#1,}%
1653     \expandafter\firstofoneargument
1654   \else
1655     \expandafter\gobbleoneargument
1656   \fi}
1657
1658%D Some more raw material:
1659
1660\def\syst_helpers_do_raw_process_action[#1][#2]%
1661  {\def\syst_helpers_do_do_raw_process_action##1,#1=>##2,##3^^^^0004%
1662     {\if##3@\else
1663        \def\m_syst_helpers_process_action{##2}%
1664      \fi}%
1665   \syst_helpers_do_do_raw_process_action,#2,#1=>,@^^^^0004}
1666
1667\protected\def\rawprocessaction[#1]#2[#3]%
1668  {\edef\m_syst_string_one{#1}%
1669   \edef\m_syst_string_two{undefined}% better \!!undefined
1670   \let\m_syst_helpers_process_action\m_syst_string_two
1671   \ifx\m_syst_string_one\empty
1672     \expandafter\syst_helpers_do_raw_process_action\expandafter[\s!default][#3]%
1673   \else
1674      \expandafter\syst_helpers_do_raw_process_action\expandafter[\m_syst_string_one][#3]%
1675      \ifx\m_syst_helpers_process_action\m_syst_string_two
1676        \expandafter\syst_helpers_do_raw_process_action\expandafter[\s!unknown][#3]%
1677      \fi
1678   \fi
1679   \ifx\m_syst_helpers_process_action\m_syst_string_two
1680   \else
1681     \m_syst_helpers_process_action
1682   \fi}
1683
1684%D When we process the list \type {a,b,c,d,e}, the raw routine takes over 30\% less
1685%D time, when we feed $20+$ character strings we gain about 20\%. Alternatives which
1686%D use \type {\futurelet} perform worse. Part of the speedup is due to the \type
1687%D {\let} and \type {\expandafter} in the test.
1688%D
1689%D \macros
1690%D   {dosetvalue,dosetevalue,dosetgvalue,docopyvalue,doresetvalue,
1691%D    dogetvalue}
1692%D
1693%D When we are going to do assignments, we have to take multi||linguality into account.
1694%D For the moment we keep things simple and single||lingual.
1695%D
1696%D \starttyping
1697%D \dosetvalue   {label}    {variable}   {value}
1698%D \dosetevalue  {label}    {variable}   {value}
1699%D \dosetgvalue  {label}    {variable}   {value}
1700%D \docopyvalue  {to label} {from label} {variable}
1701%D \doresetvalue {label}    {variable}
1702%D \stoptyping
1703%D
1704%D These macros are in fact auxiliary ones and are not meant for use outside the
1705%D assignment macros.
1706
1707\def\dosetvalue#1#2% #3
1708  {\expandafter\def\csname#1#2\endcsname} % {#3}}
1709
1710\def\dosetevalue#1#2% #3
1711  {\expandafter\edef\csname#1#2\endcsname} % {#3}}
1712
1713\def\dosetgvalue#1#2% #3
1714  {\expandafter\gdef\csname#1#2\endcsname} % {#3}}
1715
1716\def\doresetvalue#1#2%
1717  {\expandafter\let\csname#1#2\endcsname\empty}
1718
1719\def\doignorevalue#1#2#3%
1720  {\expandafter\let\csname#1#2\endcsname\empty}
1721
1722\def\docopyvalue#1#2#3%
1723  {\expandafter\def\csname#1#3\endcsname{\csname#2#3\endcsname}}
1724
1725%D Experiment:
1726
1727\edef\m_syst_protected{\detokenize{protected}}
1728
1729\protected\def\aliasmacro#1#2%
1730  {\doifinstring{\m_syst_protected}{\meaning#2}\protected
1731   \def#1{#2}}
1732
1733%D \macros
1734%D   {doassign,undoassign,doassignempty}
1735%D
1736%D Assignments are the backbone of \CONTEXT. Abhorred by the concept of style file
1737%D hacking, we took a considerable effort in building a parameterized system.
1738%D Unfortunately there is a price to pay in terms of speed. Compared to other
1739%D packages and taking the functionality of \CONTEXT\ into account, the total size
1740%D of the format file is still very acceptable. Now how are these assignments done.
1741%D
1742%D Assignments can be realized with:
1743%D
1744%D \starttyping
1745%D \doassign[label][variable=value]
1746%D \undoassign[label][variable=value]
1747%D \stoptyping
1748%D
1749%D and:
1750%D
1751%D \starttyping
1752%D \doassignempty[label][variable=value]
1753%D \stoptyping
1754%D
1755%D Assignments like \type{\doassign} are compatible with:
1756%D
1757%D \starttyping
1758%D \def\labelvariable{value}
1759%D \stoptyping
1760%D
1761%D We do check for the presence of an \type{=} and loudly complain of it's missed. We
1762%D will redefine this macro later on, when a more advanced message mechanism is
1763%D implemented.
1764
1765\protected\def\showassignerror#1#2%
1766  {\writestatus{setup}{missing or ungrouped '=' after '#1' in line #2}}
1767
1768\protected\def\doassignempty[#1][#2=#3]%
1769  {\ifcsname#1#2\endcsname\else\dosetvalue{#1}{#2}{#3}\fi}
1770
1771%D \macros
1772%D   {getparameters,geteparameters,getgparameters,
1773%D    forgetparameters}
1774%D
1775%D Using the assignment commands directly is not our ideal of user friendly interfacing,
1776%D so we take some further steps.
1777%D
1778%D \starttyping
1779%D \getparameters    [label] [...=...,...=...]
1780%D \forgetparameters [label] [...=...,...=...]
1781%D \stoptyping
1782%D
1783%D Again, the label identifies the category a variable belongs to. The second argument
1784%D can be a comma separated list of assignments.
1785%D
1786%D \starttyping
1787%D \getparameters
1788%D   [demo]
1789%D   [alfa=1,
1790%D    beta=2]
1791%D \stoptyping
1792%D
1793%D is equivalent to
1794%D
1795%D \starttyping
1796%D \def\demoalfa{1}
1797%D \def\demobeta{2}
1798%D \stoptyping
1799%D
1800%D
1801%D In the pre||multi||lingual stadium \CONTEXT\ took the next approach. With
1802%D
1803%D \starttyping
1804%D \def\??demo {@@demo}
1805%D \def\!!alfa {alfa}
1806%D \def\!!beta {beta}
1807%D \stoptyping
1808%D
1809%D calling
1810%D
1811%D \starttyping
1812%D \getparameters
1813%D   [\??demo]
1814%D   [\!!alfa=1,
1815%D    \!!beta=2]
1816%D \stoptyping
1817%D
1818%D lead to:
1819%D
1820%D \starttyping
1821%D \def\@@demoalfa{1}
1822%D \def\@@demobeta{2}
1823%D \stoptyping
1824%D
1825%D Because we want to be able to distinguish the \type{!!} pre||tagged user supplied
1826%D variables from internal counterparts, we will introduce a slightly different tag
1827%D in the multi||lingual modules. There we will use \type{c!} or \type{v!},
1828%D depending on the context.
1829%D
1830%D By calling \type{doassign} directly, we save ourselves some argument passing
1831%D and gain some speed. Whatever optimizations we do, this command will always be
1832%D one of the bigger bottlenecks. The alternative \type{\geteparameters} --- it's
1833%D funny to see that this alternative saw the light so lately --- can be used to do
1834%D expanded assigments.
1835
1836\let\currentvalue\empty
1837
1838\protected\def\getparameters   {\dogetparameters\dosetvalue}
1839\protected\def\geteparameters  {\dogetparameters\dosetevalue}
1840\protected\def\getgparameters  {\dogetparameters\dosetgvalue}
1841\protected\def\getxparameters  {\dogetparameters\dosetxvalue}
1842\protected\def\forgetparameters{\dogetparameters\doignorevalue}
1843
1844\let\getexpandedparameters\geteparameters
1845
1846\protected\def\dogetparameters#1[#2]#3[#4%
1847  {\if\noexpand#4]%
1848     \expandafter\gobbleoneargument
1849   \else
1850     \let\setsomevalue#1%
1851     \def\syst_helpers_get_parameters_assign{\syst_helpers_get_parameters_assign_indeed#2}%
1852     \expandafter\syst_helpers_get_parameters
1853   \fi#4}
1854
1855\def\syst_helpers_get_parameters#1]%
1856  {\xprocesscommaitem#1,],^^^^0004}
1857
1858\def\syst_helpers_process_comma_item#1,#2% #2 takes space before ,
1859  {\if,#1,% dirty trick for testing #1=empty
1860     \expandafter\syst_helpers_process_comma_item
1861   \else\if]#1%
1862     \doubleexpandafter\gobbleoneargument
1863   \else
1864     \syst_helpers_get_parameters_assign^^^^0004#1==\empty^^^^0004%
1865     \doubleexpandafter\syst_helpers_process_comma_item
1866   \fi\fi#2}
1867
1868\def\syst_helpers_assign_error#1#2#3%
1869  {\showassignerror{#2}{\the\inputlineno\space(#1)}}
1870
1871\def\syst_helpers_get_parameters_assign_normal#1^^^^0004#2=#3=#4#5^^^^0004%
1872  {\ifx\empty#2\empty
1873     \expandafter\syst_helpers_assign_error
1874   \else\ifx#4\empty
1875     \doubleexpandafter\syst_helpers_assign_error
1876   \else
1877     \doubleexpandafter\setsomevalue
1878   \fi\fi
1879   {#1}{#2}{#3}}
1880
1881\def\syst_helpers_get_parameters_assign_error#1^^^^0004#2=#3=#4#5^^^^0004%
1882  {\ifx\empty#2\empty
1883     \expandafter\syst_helpers_assign_error
1884   \else\ifx#4\empty
1885     \doubleexpandafter\syst_helpers_assign_error
1886   \else
1887     \ifcsname#1#2\endcsname
1888       \expandafter\let\expandafter\currentvalue\csname#1#2\endcsname
1889     \else
1890       \let\currentvalue\empty
1891     \fi
1892     \doubleexpandafter\setsomevalue
1893   \fi\fi
1894   {#1}{#2}{#3}}
1895
1896\let\syst_helpers_get_parameters_assign_indeed\syst_helpers_get_parameters_assign_normal
1897
1898\protected\def\doassign  [#1][#2]{\let\setsomevalue\dosetvalue  \syst_helpers_get_parameters_assign_indeed#1^^^^0004#2==\empty^^^^0004}
1899\protected\def\doeassign [#1][#2]{\let\setsomevalue\dosetevalue \syst_helpers_get_parameters_assign_indeed#1^^^^0004#2==\empty^^^^0004}
1900\protected\def\undoassign[#1][#2]{\let\setsomevalue\doresetvalue\syst_helpers_get_parameters_assign_indeed#1^^^^0004#2==\empty^^^^0004}
1901
1902%D \macros
1903%D   {processassignmentlist,processassignmentcommand,
1904%D    startprocessassignmentlist,startprocessassignmentcommand}
1905%D
1906%D For Wolfgang:
1907%D
1908%D \starttyping
1909%D \def\showpair#1#2{key:#1, value:#2\par}
1910%D \processassignmentlist[a=1,b=2]\showpair
1911%D \stoptyping
1912%D
1913%D We can optimize this one if needed but it's not a core macro so hardly worth the
1914%D trouble and tokens.
1915
1916\protected\def\processassignmentlist[#1]#2% #2 == \command{key}{value]
1917  {\def\syst_helpers_process_assignment_entry##1{#2}% {##2}{##3} % namespace is ignored
1918   \dogetparameters\syst_helpers_process_assignment_entry[][#1]}
1919
1920\protected\def\processassignmentcommand[#1]%
1921  {\normalexpanded{\processassignmentlist[#1]}}
1922
1923\protected\def\startprocessassignmentlist[#1]#2\stopprocessassignmentlist
1924  {\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}%
1925   \processassignmentlist[#1]\currentassignmentlistcommand}
1926
1927\protected\def\startprocessassignmentcommand[#1]#2\stopprocessassignmentcommand
1928  {\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}%
1929   \normalexpanded{\processassignmentlist[#1]}\currentassignmentlistcommand}
1930
1931%D \macros{currentvalue}
1932%D
1933%D Just in case a \type{\getparameter} argument itself ends up inside a \type
1934%D {\write} or other expandable location, our new macro needs a default value.
1935%D
1936%D \starttyping
1937%D \getparameters[xxx][aaa=bbb]\par
1938%D \getparameters[xxx][=bbb]\par
1939%D \getparameters[xxx][aaa=]\par
1940%D \getparameters[xxx][=]\par
1941%D \getparameters[xxx][aaa]\par
1942%D \stoptyping
1943
1944%D \macros
1945%D   {expandparameters}
1946%D
1947%D Example usage:
1948%D
1949%D \startbuffer
1950%D \getparameters[taco][name=taco]
1951%D \convertcommand\taconame\to\ascii \ascii
1952%D \expandparameters \getparameters[taco][name=\currentvalue\space hoekwater]
1953%D \convertcommand\taconame\to\ascii \ascii
1954%D \getparameters[taco][name=\currentvalue\space hoekwater]
1955%D \convertcommand\taconame\to\ascii \ascii
1956%D \stopbuffer
1957%D
1958%D \typebuffer
1959%D \startlines
1960%D \getbuffer
1961%D \stoplines
1962%D
1963%D Here we hook in the code (beware, this is the optimized get **):
1964
1965\def\syst_helpers_get_parameters_normal#1]%
1966  {\syst_helpers_process_comma_item#1,],^^^^0004}
1967
1968\def\syst_helpers_get_parameters_expanded#1]%
1969  {\let\dosetnvalue\setsomevalue
1970   \let\setsomevalue\dosetevalue
1971   \let\syst_helpers_get_parameters_assign_indeed\syst_helpers_get_parameters_assign_error
1972   \let\setsomevalue\dosetevalue
1973   \syst_helpers_process_comma_item#1,],^^^^0004%
1974   \let\syst_helpers_get_parameters_assign_indeed\syst_helpers_get_parameters_assign_normal
1975   \let\setsomevalue\dosetnvalue
1976   \let\syst_helpers_get_parameters\syst_helpers_get_parameters_normal
1977   \let\currentvalue\empty}
1978
1979\let\syst_helpers_get_parameters\syst_helpers_get_parameters_normal % **
1980
1981\protected\def\expandparameters
1982  {\let\syst_helpers_get_parameters\syst_helpers_get_parameters_expanded}
1983
1984%D \macros
1985%D   {getemptyparameters}
1986%D
1987%D Sometimes we explicitly want variables to default to an empty string, so we
1988%D welcome:
1989%D
1990%D \starttyping
1991%D \getemptyparameters [label] [...=...,...=...]
1992%D \stoptyping
1993
1994\protected\def\getemptyparameters[#1]#2[#3]%
1995  {\def\syst_helpers_get_empty_parameters##1{\doassignempty[#1][##1]}%
1996   \processcommalist[#3]\syst_helpers_get_empty_parameters}
1997
1998%D \macros
1999%D   {copyparameters}
2000%D
2001%D Some \CONTEXT\ commands take their default setups from others. All commands that
2002%D are able to provide backgounds or rules around some content, for instance default
2003%D to the standard command for ruled boxes. Is situations like this we can use:
2004%D
2005%D \starttyping
2006%D \copyparameters [to-label] [from-label] [name1,name2,...]
2007%D \stoptyping
2008%D
2009%D For instance
2010%D
2011%D \starttyping
2012%D \copyparameters
2013%D   [internal][external]
2014%D   [alfa,beta]
2015%D \stoptyping
2016%D
2017%D Leads to:
2018%D
2019%D \starttyping
2020%D \def\internalalfa {\externalalfa}
2021%D \def\internalbeta {\externalbeta}
2022%D \stoptyping
2023%D
2024%D By using \type {\docopyvalue} we've prepared this command for use in a
2025%D multi||lingual environment.
2026
2027\protected\def\copyparameters[#1]#2[#3]#4[#5]%
2028  {\doifnot{#1}{#3}
2029     {\def\syst_helpers_copy_parameter{\docopyvalue{#1}{#3}}% ##1
2030      \processcommalist[#5]\syst_helpers_copy_parameter}}
2031
2032%D \macros
2033%D   {ifparameters,checkparameters}
2034%D
2035%D A slightly different one is \type {\checkparameters}, which also checks on the
2036%D presence of a~\type {=}.
2037%D
2038%D The boolean \type {\ifparameters} can be used afterwards. Combining both in one
2039%D \type {\if}||macro would lead to problems with nested \type {\if}'s.
2040%D
2041%D \starttyping
2042%D \checkparameters[argument]
2043%D \stoptyping
2044
2045\newif\ifparameters
2046
2047\def\syst_helpers_check_parameters#1=#2#3^^^^0004%
2048  {\if#2^^^^0003\parametersfalse\else\parameterstrue\fi}
2049
2050\protected\def\checkparameters[#1]%
2051  {\syst_helpers_check_parameters#1=^^^^0003^^^^0003^^^^0004}
2052
2053%D \macros
2054%D   {getfromcommalist,getfromcommacommand,
2055%D    commalistelement,
2056%D    getcommalistsize,getcommacommandsize}
2057%D
2058%D It's possible to get an element from a commalist or a command representing
2059%D a commalist.
2060%D
2061%D \starttyping
2062%D \getfromcommalist    [string] [n]
2063%D \getfromcommacommand [string,\strings,string,...] [n]
2064%D \stoptyping
2065%D
2066%D The difference betwee the two of them is the same as the difference between
2067%D \type {\processcomma...}. The found string is stored in \type
2068%D {\commalistelement}.
2069%D
2070%D We can calculate the size of a comma separated list by using:
2071%D
2072%D \starttyping
2073%D \getcommalistsize    [string,string,...]
2074%D \getcommacommandsize [string,\strings,string,...]
2075%D \stoptyping
2076%D
2077%D Afterwards, the length is available in the macro \type {\commalistsize}
2078%D (not a \COUNTER).
2079
2080\newcount\commalistcounter
2081
2082\def\commalistsize{0}
2083
2084\def\syst_helpers_get_comma_list_size#1%
2085  {\advance\commalistcounter\plusone}
2086
2087\protected\def\getcommalistsize#1]% don't loose [{#1}]
2088  {\commalistcounter\zerocount
2089   \processcommalist#1]\syst_helpers_get_comma_list_size   % was [{#1}]
2090   \edef\commalistsize{\the\commalistcounter}}
2091
2092% \def\getcommacommandsize[#1]%
2093%   {\edef\commacommand{#1}%
2094%    \scratchtoks\expandafter{\expandafter[\commacommand]}%
2095%    \expandafter\getcommalistsize\the\scratchtoks }
2096
2097\protected\def\getcommacommandsize[#1]%
2098  {\normalexpanded{\getcommalistsize[#1]}}
2099
2100\def\syst_helpers_get_from_comma_list#1%
2101  {\advance\commalistcounter \minusone
2102   \ifcase\commalistcounter
2103     \def\commalistelement{#1}%
2104     \expandafter\quitcommalist
2105   \fi}
2106
2107\protected\def\getfromcommalist[#1]#2[#3]%
2108  {\let\commalistelement\empty
2109   \commalistcounter#3\relax
2110   \processcommalist[#1]\syst_helpers_get_from_comma_list}
2111
2112\protected\def\getfromcommacommand[#1]%
2113  {\normalexpanded{\getfromcommalist[#1]}}
2114
2115%D Watertight (and efficient) solutions are hard to find, due to the handling of
2116%D braces during parameters passing and scanning. Nevertheless:
2117%D
2118%D \startbuffer
2119%D \def\dosomething#1{(#1=\commalistsize) }
2120%D
2121%D \getcommalistsize [\hbox{$a,b,c,d,e,f$}] \dosomething 1
2122%D \getcommalistsize [{a,b,c,d,e,f}]        \dosomething 1
2123%D \getcommalistsize [{a,b,c},d,e,f]        \dosomething 4
2124%D \getcommalistsize [a,b,{c,d,e},f]        \dosomething 4
2125%D \getcommalistsize [a{b,c},d,e,f]         \dosomething 4
2126%D \getcommalistsize [{a,b}c,d,e,f]         \dosomething 4
2127%D \getcommalistsize []                     \dosomething 0
2128%D \getcommalistsize [{[}]                  \dosomething 1
2129%D \stopbuffer
2130%D
2131%D \typebuffer
2132%D
2133%D reports:
2134%D
2135%D \getbuffer
2136
2137%D \macros
2138%D   {dogetcommalistelement,dogetcommacommandelement}
2139%D
2140%D For low level (fast) purposes, we can also use the next alternative, which can
2141%D handle 8~elements at most.
2142%D
2143%D \starttyping
2144%D \dogetcommalistelement1\from a,b,c\to\commalistelement
2145%D \stoptyping
2146
2147\def\syst_helpers_get_comma_list_element#1\from#2,#3,#4,#5,#6,#7,#8\to#9%
2148  {\edef#9{\ifcase#1\relax\or#2\or#3\or#4\or#5\or#6\or#7\or#8\fi}}
2149
2150\def\dogetcommacommandelement#1\from#2\to%
2151  {\expandafter\syst_helpers_get_comma_list_element\expandafter#1\expandafter\from#2,,,,,,\to}
2152
2153%D \macros
2154%D   {dosingleargument,dodoubleargument,dotripleargument,
2155%D    doquadrupleargument,doquintupleargument,dosixtupleargument,
2156%D    doseventupleargument}
2157%D
2158%D When working with delimited arguments, spaces and lineendings can interfere. The
2159%D next set of macros uses \TEX' internal scanner for grabbing everything between
2160%D arguments. Forgive me the funny names.
2161%D
2162%D \starttyping
2163%D \dosingleargument\commando    = \commando[#1]
2164%D \dodoubleargument\commando    = \commando[#1][#2]
2165%D \dotripleargument\commando    = \commando[#1][#2][#3]
2166%D \doquadrupleargument\commando = \commando[#1][#2][#3][#4]
2167%D \doquintupleargument\commando = \commando[#1][#2][#3][#4][#5]
2168%D \dosixtupleargument\commando  = \commando[#1][#2][#3][#4][#5][#6]
2169%D \doseventupleargument\command = \commando[#1][#2][#3][#4][#5][#6][#7]
2170%D \stoptyping
2171%D
2172%D These macros are used in the following way:
2173%D
2174%D \starttyping
2175%D \def\dosetupsomething[#1][#2]%
2176%D   {... #1 ... #2 ...}
2177%D
2178%D \protected\def\setupsomething
2179%D   {\dodoubleargument\dosetupsomething}
2180%D \stoptyping
2181%D
2182%D The implementation can be surprisingly simple and needs no
2183%D further explanation, like:
2184%D
2185%D \starttyping
2186%D \def\dosingleargument#1[#2]%
2187%D   {#1[#2]}
2188%D \def\dotripleargument#1[#2]#3[#4]#5[#6]%
2189%D   {#1[#2][#4][#6]}
2190%D \def\doquintupleargument#1%
2191%D   {\def\dodoquintupleargument[##1]##2[##3]##4[##5]##6[##7]##8[##9]%
2192%D      {#1[##1][##3][##5][##7][##9]}%
2193%D    \dodoquintupleargument}
2194%D \stoptyping
2195%D
2196%D Because \TEX\ accepts 9~arguments at most, we have to use two||step solution when
2197%D getting five or more arguments.
2198%D
2199%D When developing more and more of the real \CONTEXT, we started using some
2200%D alternatives that provided empty arguments (in fact optional ones) whenever the
2201%D user failed to supply them. Because this more complicated macros enable us to do
2202%D some checking, we reimplemented the non||empty ones.
2203
2204% no longer a message:
2205%
2206% \protected\def\dosingleargument    {\let\expectedarguments\plusone   \dosingleempty    }
2207% \protected\def\dodoubleargument    {\let\expectedarguments\plustwo   \dodoubleempty    }
2208% \protected\def\dotripleargument    {\let\expectedarguments\plusthree \dotripleempty    }
2209% \protected\def\doquadrupleargument {\let\expectedarguments\plusfour  \doquadrupleempty }
2210% \protected\def\doquintupleargument {\let\expectedarguments\plusfive  \doquintupleempty }
2211% \protected\def\dosixtupleargument  {\let\expectedarguments\plussix   \dosixtupleempty  }
2212% \protected\def\doseventupleargument{\let\expectedarguments\plusseven \doseventupleempty}
2213
2214%D \macros
2215%D   {iffirstagument,ifsecondargument,ifthirdargument,
2216%D    iffourthargument,iffifthargument,ifsixthargument,
2217%D    ifseventhargument}
2218%D
2219%D We use some signals for telling the calling macros if all wanted arguments are
2220%D indeed supplied by the user.
2221
2222\newif\iffirstargument
2223\newif\ifsecondargument
2224\newif\ifthirdargument
2225\newif\iffourthargument
2226\newif\iffifthargument
2227\newif\ifsixthargument
2228\newif\ifseventhargument
2229
2230%D \macros
2231%D   {dosingleempty,dodoubleempty,dotripleempty,
2232%D    doquadrupleempty,doquintupleempty,dosixtupeempty,
2233%D    doseventupleempty}
2234%D
2235%D The empty argument supplying macros mentioned before, look like:
2236%D
2237%D \starttyping
2238%D \dosingleempty    \command
2239%D \dodoubleempty    \command
2240%D \dotripleempty    \command
2241%D \doquadrupleempty \command
2242%D \doquintupleempty \command
2243%D \dosixtuple_empty  \command
2244%D \doseventupleempty\command
2245%D \stoptyping
2246%D
2247%D So \type{\dodoubleempty} leads to:
2248%D
2249%D \starttyping
2250%D \command[#1][#2]
2251%D \command[#1][]
2252%D \command[][]
2253%D \stoptyping
2254%D
2255%D Depending of the generousity of the user. Afterwards one can use the \type
2256%D {\if...argument} boolean. For novice: watch the stepwise doubling of \type {#}'s.
2257
2258% \setnewconstant\noexpectedarguments\zerocount
2259% \setnewconstant\expectedarguments  \zerocount
2260%
2261% \protected\def\showargumenterror#1#2%
2262%   {\writestatus{system}{\number#1 argument(s) expected in line #2}}
2263%
2264% \protected\def\syst_helpers_argument_error
2265%   {\ifnum\expectedarguments>\noexpectedarguments
2266%      \showargumenterror{\number\expectedarguments}{\number\inputlineno}%
2267%    \fi
2268%    \syst_helpers_argument_reset}
2269%
2270% \protected\def\syst_helpers_argument_reset
2271%   {\let\expectedarguments\noexpectedarguments}
2272
2273% \def\test[#1]{(#1)}
2274%
2275% \dosingleempty\test[]   xxx\par
2276% \dosingleempty\test     xxx\par
2277%
2278% \def\test[#1][#2]{(#1,#2)}
2279%
2280% \dodoubleempty\test[][] xxx\par
2281% \dodoubleempty\test[]   xxx\par
2282% \dodoubleempty\test     xxx\par
2283%
2284% \def\test[#1][#2][#3]{(#1,#2,#3)}
2285%
2286% \dotripleempty\test[][][] xxx\par
2287% \dotripleempty\test[][]   xxx\par
2288% \dotripleempty\test[]     xxx\par
2289% \dotripleempty\test       xxx\par
2290
2291%D Common:
2292
2293\newtoks\t_syst_aux
2294
2295% \def\syst_helpers_empty_spaced_six  {\expandafter\m_syst_aux_do\the\t_syst_aux[][][][][][] }
2296% \def\syst_helpers_empty_normal_six  {\expandafter\m_syst_aux_do\the\t_syst_aux[][][][][][]}
2297% \def\syst_helpers_empty_spaced_five {\expandafter\m_syst_aux_do\the\t_syst_aux[][][][][] }
2298% \def\syst_helpers_empty_normal_five {\expandafter\m_syst_aux_do\the\t_syst_aux[][][][][]}
2299% \def\syst_helpers_empty_spaced_four {\expandafter\m_syst_aux_do\the\t_syst_aux[][][][] }
2300% \def\syst_helpers_empty_normal_four {\expandafter\m_syst_aux_do\the\t_syst_aux[][][][]}
2301% \def\syst_helpers_empty_spaced_three{\expandafter\m_syst_aux_do\the\t_syst_aux[][][] }
2302% \def\syst_helpers_empty_normal_three{\expandafter\m_syst_aux_do\the\t_syst_aux[][][]}
2303% \def\syst_helpers_empty_spaced_two  {\expandafter\m_syst_aux_do\the\t_syst_aux[][] }
2304% \def\syst_helpers_empty_normal_two  {\expandafter\m_syst_aux_do\the\t_syst_aux[][]}
2305% \def\syst_helpers_empty_spaced_one  {\expandafter\m_syst_aux_do\the\t_syst_aux[] }
2306% \def\syst_helpers_empty_normal_one  {\expandafter\m_syst_aux_do\the\t_syst_aux[]}
2307%
2308% \def\syst_helpers_single_empty_one_yes      {\firstargumenttrue              \m_syst_aux_do}
2309% \def\syst_helpers_double_empty_two_yes      {\secondargumenttrue \expandafter\m_syst_aux_do\the\t_syst_aux}
2310% \def\syst_helpers_triple_empty_three_yes    {\thirdargumenttrue  \expandafter\m_syst_aux_do\the\t_syst_aux}
2311% \def\syst_helpers_quadruple_empty_four_yes  {\fourthargumenttrue \expandafter\m_syst_aux_do\the\t_syst_aux}
2312% \def\syst_helpers_quintuple_empty_five_yes  {\fifthargumenttrue  \expandafter\m_syst_aux_do\the\t_syst_aux}
2313% \def\syst_helpers_sixtuple_empty_six_yes    {\sixthargumenttrue  \expandafter\m_syst_aux_do\the\t_syst_aux}
2314% \def\syst_helpers_seventuple_empty_seven_yes{\seventhargumenttrue\expandafter\m_syst_aux_do\the\t_syst_aux}
2315%
2316% with
2317%
2318% \protected\def\dodoubleempty#1%
2319%   {\syst_helpers_argument_reset
2320%    \let\m_syst_aux_do#1% alias
2321%    \let\m_syst_action_yes\syst_helpers_double_empty_one_yes
2322%    \let\m_syst_action_nop\syst_helpers_double_empty_one_nop
2323%    \let\if_next_blank_space_token\iffalse
2324%    \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2325%
2326% \def\syst_helpers_double_empty_one_yes[#1]%
2327%   {\firstargumenttrue
2328%    \t_syst_aux{[{#1}]}% assignment
2329%    \let\m_syst_action_yes\syst_helpers_double_empty_two_yes
2330%    \let\m_syst_action_nop\syst_helpers_double_empty_two_nop
2331%    \let\if_next_blank_space_token\iffalse
2332%    \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2333%
2334% But we use this as it keeps the original name visible:
2335
2336\def\syst_helpers_empty_spaced_six  {\the\t_syst_aux[][][][][][] }
2337\def\syst_helpers_empty_normal_six  {\the\t_syst_aux[][][][][][]}
2338\def\syst_helpers_empty_spaced_five {\the\t_syst_aux[][][][][] }
2339\def\syst_helpers_empty_normal_five {\the\t_syst_aux[][][][][]}
2340\def\syst_helpers_empty_spaced_four {\the\t_syst_aux[][][][] }
2341\def\syst_helpers_empty_normal_four {\the\t_syst_aux[][][][]}
2342\def\syst_helpers_empty_spaced_three{\the\t_syst_aux[][][] }
2343\def\syst_helpers_empty_normal_three{\the\t_syst_aux[][][]}
2344\def\syst_helpers_empty_spaced_two  {\the\t_syst_aux[][] }
2345\def\syst_helpers_empty_normal_two  {\the\t_syst_aux[][]}
2346\def\syst_helpers_empty_spaced_one  {\the\t_syst_aux[] }
2347\def\syst_helpers_empty_normal_one  {\the\t_syst_aux[]}
2348
2349\def\syst_helpers_single_empty_one_yes      {\firstargumenttrue  \the\t_syst_aux}
2350\def\syst_helpers_double_empty_two_yes      {\secondargumenttrue \the\t_syst_aux}
2351\def\syst_helpers_triple_empty_three_yes    {\thirdargumenttrue  \the\t_syst_aux}
2352\def\syst_helpers_quadruple_empty_four_yes  {\fourthargumenttrue \the\t_syst_aux}
2353\def\syst_helpers_quintuple_empty_five_yes  {\fifthargumenttrue  \the\t_syst_aux}
2354\def\syst_helpers_sixtuple_empty_six_yes    {\sixthargumenttrue  \the\t_syst_aux}
2355\def\syst_helpers_seventuple_empty_seven_yes{\seventhargumenttrue\the\t_syst_aux}
2356
2357%D Single:
2358
2359% \protected\def\dosingleempty#1%
2360%   {\syst_helpers_argument_reset
2361%    \doifelsenextoptional
2362%      {\firstargumenttrue#1}%
2363%      {\syst_helpers_single_empty_one_nop#1}}
2364%
2365% \def\syst_helpers_single_empty_one_nop#1%
2366%   {\firstargumentfalse
2367%    #1[]}
2368
2369\protected\def\dosingleempty#1%
2370  {%syst_helpers_argument_reset
2371   \t_syst_aux{#1}%
2372   \let\m_syst_action_yes\syst_helpers_single_empty_one_yes
2373   \let\m_syst_action_nop\syst_helpers_single_empty_one_nop
2374   \let\if_next_blank_space_token\iffalse
2375   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2376
2377\def\syst_helpers_single_empty_one_nop
2378  {\firstargumentfalse
2379   \the\t_syst_aux[]}
2380
2381%D Double
2382
2383% \protected\def\dodoubleempty#1%
2384%   {\syst_helpers_argument_reset
2385%    \doifelsenextoptional
2386%      {\syst_helpers_double_empty_one_yes#1}%
2387%      {\syst_helpers_double_empty_one_nop#1}}
2388%
2389% \def\syst_helpers_double_empty_one_yes#1[#2]%
2390%   {\firstargumenttrue
2391%    \doifelsenextoptional
2392%      {\secondargumenttrue#1[{#2}]}%
2393%      {\syst_helpers_double_empty_two_nop#1{#2}}}
2394%
2395% \def\syst_helpers_double_empty_one_nop#1%
2396%   {\firstargumentfalse
2397%    \secondargumentfalse
2398%    #1[][]}
2399%
2400% \def\syst_helpers_double_empty_two_nop
2401%   {\secondargumentfalse
2402%    \if_next_blank_space_token
2403%      \expandafter\syst_helpers_double_empty_one_spaced
2404%    \else
2405%      \expandafter\syst_helpers_double_empty_one_normal
2406%    \fi}
2407%
2408% \def\syst_helpers_double_empty_one_spaced#1#2{#1[{#2}][] }
2409% \def\syst_helpers_double_empty_one_normal#1#2{#1[{#2}][]}
2410
2411\protected\def\dodoubleempty#1%
2412  {%syst_helpers_argument_reset
2413   \t_syst_aux{#1}%
2414   \let\m_syst_action_yes\syst_helpers_double_empty_one_yes
2415   \let\m_syst_action_nop\syst_helpers_double_empty_one_nop
2416   \let\if_next_blank_space_token\iffalse
2417   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2418
2419\def\syst_helpers_double_empty_one_yes[#1]%
2420  {\firstargumenttrue
2421   \toksapp\t_syst_aux{[{#1}]}%
2422   \let\m_syst_action_yes\syst_helpers_double_empty_two_yes
2423   \let\m_syst_action_nop\syst_helpers_double_empty_two_nop
2424   \let\if_next_blank_space_token\iffalse
2425   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2426
2427\def\syst_helpers_double_empty_one_nop
2428  {\firstargumentfalse
2429   \secondargumentfalse
2430   \the\t_syst_aux[][]}
2431
2432\def\syst_helpers_double_empty_two_nop
2433  {\secondargumentfalse
2434   \if_next_blank_space_token
2435     \expandafter\syst_helpers_empty_spaced_one
2436   \else
2437     \expandafter\syst_helpers_empty_normal_one
2438   \fi}
2439
2440% Triple
2441
2442% \protected\def\dotripleempty#1%
2443%   {\syst_helpers_argument_reset
2444%    \doifelsenextoptional
2445%      {\syst_helpers_triple_empty_one_yes#1}%
2446%      {\syst_helpers_triple_empty_one_nop#1}}
2447%
2448% \def\syst_helpers_triple_empty_one_yes#1[#2]%
2449%   {\firstargumenttrue
2450%    \doifelsenextoptional
2451%      {\syst_helpers_triple_empty_two_yes#1{#2}}%
2452%      {\syst_helpers_triple_empty_two_nop#1{#2}}}
2453%
2454% \def\syst_helpers_triple_empty_two_yes#1#2[#3]%
2455%   {\secondargumenttrue
2456%    \doifelsenextoptional
2457%      {\thirdargumenttrue#1[{#2}][{#3}]}%
2458%      {\syst_helpers_triple_empty_three_nop#1{#2}{#3}}}
2459%
2460% \def\syst_helpers_triple_empty_one_nop#1%
2461%   {\firstargumentfalse
2462%    \secondargumentfalse
2463%    \thirdargumentfalse
2464%    #1[][][]}
2465%
2466% \def\syst_helpers_triple_empty_two_nop
2467%   {\secondargumentfalse
2468%    \thirdargumentfalse
2469%    \if_next_blank_space_token
2470%      \expandafter\syst_helpers_triple_empty_two_spaced
2471%    \else
2472%      \expandafter\syst_helpers_triple_empty_two_normal
2473%    \fi}
2474%
2475% \def\syst_helpers_triple_empty_three_nop
2476%   {\thirdargumentfalse
2477%    \if_next_blank_space_token
2478%      \expandafter\syst_helpers_triple_empty_three_spaced
2479%    \else
2480%      \expandafter\syst_helpers_triple_empty_three_normal
2481%    \fi}
2482%
2483% \def\syst_helpers_triple_empty_two_spaced  #1#2{#1[{#2}][][] }
2484% \def\syst_helpers_triple_empty_two_normal  #1#2{#1[{#2}][][]}
2485% \def\syst_helpers_triple_empty_three_spaced#1#2#3{#1[{#2}][{#3}][] }
2486% \def\syst_helpers_triple_empty_three_normal#1#2#3{#1[{#2}][{#3}][]}
2487
2488\protected\def\dotripleempty#1%
2489  {%syst_helpers_argument_reset
2490   \t_syst_aux{#1}%
2491   \let\m_syst_action_yes\syst_helpers_triple_empty_one_yes
2492   \let\m_syst_action_nop\syst_helpers_triple_empty_one_nop
2493   \let\if_next_blank_space_token\iffalse
2494   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2495
2496\def\syst_helpers_triple_empty_one_yes[#1]%
2497  {\firstargumenttrue
2498   \toksapp\t_syst_aux{[{#1}]}%
2499   \let\m_syst_action_yes\syst_helpers_triple_empty_two_yes
2500   \let\m_syst_action_nop\syst_helpers_triple_empty_two_nop
2501   \let\if_next_blank_space_token\iffalse
2502   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2503
2504\def\syst_helpers_triple_empty_two_yes[#1]%
2505  {\secondargumenttrue
2506   \toksapp\t_syst_aux{[{#1}]}%
2507   \let\m_syst_action_yes\syst_helpers_triple_empty_three_yes
2508   \let\m_syst_action_nop\syst_helpers_triple_empty_three_nop
2509   \let\if_next_blank_space_token\iffalse
2510   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2511
2512\def\syst_helpers_triple_empty_one_nop
2513  {\firstargumentfalse
2514   \secondargumentfalse
2515   \thirdargumentfalse
2516   \the\t_syst_aux[][][]}
2517
2518\def\syst_helpers_triple_empty_two_nop
2519  {\secondargumentfalse
2520   \thirdargumentfalse
2521   \if_next_blank_space_token
2522     \expandafter\syst_helpers_empty_spaced_two
2523   \else
2524     \expandafter\syst_helpers_empty_normal_two
2525   \fi}
2526
2527\def\syst_helpers_triple_empty_three_nop
2528  {\thirdargumentfalse
2529   \if_next_blank_space_token
2530     \expandafter\syst_helpers_empty_spaced_one
2531   \else
2532     \expandafter\syst_helpers_empty_normal_one
2533   \fi}
2534
2535%D Quadruple:
2536
2537% \protected\def\doquadrupleempty#1%
2538%   {\syst_helpers_argument_reset
2539%    \doifelsenextoptional
2540%      {\syst_helpers_quadruple_empty_one_yes#1}%
2541%      {\syst_helpers_quadruple_empty_one_nop#1}}
2542%
2543% \def\syst_helpers_quadruple_empty_one_yes#1[#2]%
2544%   {\firstargumenttrue
2545%    \doifelsenextoptional
2546%      {\syst_helpers_quadruple_empty_two_yes#1{#2}}%
2547%      {\syst_helpers_quadruple_empty_two_nop#1{#2}}}
2548%
2549% \def\syst_helpers_quadruple_empty_two_yes#1#2[#3]%
2550%   {\secondargumenttrue
2551%    \doifelsenextoptional
2552%      {\syst_helpers_quadruple_empty_three_yes#1{#2}{#3}}%
2553%      {\syst_helpers_quadruple_empty_three_nop#1{#2}{#3}}}
2554%
2555% \def\syst_helpers_quadruple_empty_three_yes#1#2#3[#4]%
2556%   {\thirdargumenttrue
2557%    \doifelsenextoptional
2558%      {\fourthargumenttrue#1[{#2}][{#3}][{#4}]}%
2559%      {\syst_helpers_quadruple_empty_four_nop#1{#2}{#3}{#4}}}
2560%
2561% \def\syst_helpers_quadruple_empty_one_nop#1%
2562%   {\firstargumentfalse
2563%    \secondargumentfalse
2564%    \thirdargumentfalse
2565%    \fourthargumentfalse
2566%    #1[][][][]}
2567%
2568% \def\syst_helpers_quadruple_empty_two_nop
2569%   {\secondargumentfalse
2570%    \thirdargumentfalse
2571%    \fourthargumentfalse
2572%    \if_next_blank_space_token
2573%      \expandafter\syst_helpers_quadruple_empty_two_spaced
2574%    \else
2575%      \expandafter\syst_helpers_quadruple_empty_two_normal
2576%    \fi}
2577%
2578% \def\syst_helpers_quadruple_empty_three_nop
2579%   {\thirdargumentfalse
2580%    \fourthargumentfalse
2581%    \if_next_blank_space_token
2582%      \expandafter\syst_helpers_quadruple_empty_three_spaced
2583%    \else
2584%      \expandafter\syst_helpers_quadruple_empty_three_normal
2585%    \fi}
2586%
2587% \def\syst_helpers_quadruple_empty_four_nop
2588%   {\fourthargumentfalse
2589%    \if_next_blank_space_token
2590%      \expandafter\syst_helpers_quadruple_empty_four_spaced
2591%    \else
2592%      \expandafter\syst_helpers_quadruple_empty_four_normal
2593%    \fi}
2594%
2595% \def\syst_helpers_quadruple_empty_two_spaced      #1#2{#1[{#2}][][][] }
2596% \def\syst_helpers_quadruple_empty_two_normal      #1#2{#1[{#2}][][][]}
2597% \def\syst_helpers_quadruple_empty_three_spaced  #1#2#3{#1[{#2}][{#3}][][] }
2598% \def\syst_helpers_quadruple_empty_three_normal  #1#2#3{#1[{#2}][{#3}][][]}
2599% \def\syst_helpers_quadruple_empty_four_spaced #1#2#3#4{#1[{#2}][{#3}][{#4}][] }
2600% \def\syst_helpers_quadruple_empty_four_normal #1#2#3#4{#1[{#2}][{#3}][{#4}][]}
2601
2602\protected\def\doquadrupleempty#1%
2603  {%syst_helpers_argument_reset
2604   \t_syst_aux{#1}%
2605   \let\m_syst_action_yes\syst_helpers_quadruple_empty_one_yes
2606   \let\m_syst_action_nop\syst_helpers_quadruple_empty_one_nop
2607   \let\if_next_blank_space_token\iffalse
2608   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2609
2610\def\syst_helpers_quadruple_empty_one_yes[#1]%
2611  {\firstargumenttrue
2612   \toksapp\t_syst_aux{[{#1}]}%
2613   \let\m_syst_action_yes\syst_helpers_quadruple_empty_two_yes
2614   \let\m_syst_action_nop\syst_helpers_quadruple_empty_two_nop
2615   \let\if_next_blank_space_token\iffalse
2616   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2617
2618\def\syst_helpers_quadruple_empty_two_yes[#1]%
2619  {\secondargumenttrue
2620   \toksapp\t_syst_aux{[{#1}]}%
2621   \let\m_syst_action_yes\syst_helpers_quadruple_empty_three_yes
2622   \let\m_syst_action_nop\syst_helpers_quadruple_empty_three_nop
2623   \let\if_next_blank_space_token\iffalse
2624   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2625
2626\def\syst_helpers_quadruple_empty_three_yes[#1]%
2627  {\thirdargumenttrue
2628   \toksapp\t_syst_aux{[{#1}]}%
2629   \let\m_syst_action_yes\syst_helpers_quadruple_empty_four_yes
2630   \let\m_syst_action_nop\syst_helpers_quadruple_empty_four_nop
2631   \let\if_next_blank_space_token\iffalse
2632   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2633
2634\def\syst_helpers_quadruple_empty_one_nop
2635  {\firstargumentfalse
2636   \secondargumentfalse
2637   \thirdargumentfalse
2638   \fourthargumentfalse
2639   \the\t_syst_aux[][][][]}
2640
2641\def\syst_helpers_quadruple_empty_two_nop
2642  {\secondargumentfalse
2643   \thirdargumentfalse
2644   \fourthargumentfalse
2645   \if_next_blank_space_token
2646     \expandafter\syst_helpers_empty_spaced_three
2647   \else
2648     \expandafter\syst_helpers_empty_normal_three
2649   \fi}
2650
2651\def\syst_helpers_quadruple_empty_three_nop
2652  {\thirdargumentfalse
2653   \fourthargumentfalse
2654   \if_next_blank_space_token
2655     \expandafter\syst_helpers_empty_spaced_two
2656   \else
2657     \expandafter\syst_helpers_empty_normal_two
2658   \fi}
2659
2660\def\syst_helpers_quadruple_empty_four_nop
2661  {\fourthargumentfalse
2662   \if_next_blank_space_token
2663     \expandafter\syst_helpers_empty_spaced_one
2664   \else
2665     \expandafter\syst_helpers_empty_normal_one
2666   \fi}
2667
2668%D Quintuple:
2669
2670% \protected\def\doquintupleempty#1%
2671%   {\syst_helpers_argument_reset
2672%    \doifelsenextoptional
2673%      {\syst_helpers_quintuple_empty_one_yes#1}%
2674%      {\syst_helpers_quintuple_empty_one_nop#1}}
2675%
2676% \def\syst_helpers_quintuple_empty_one_yes#1[#2]%
2677%   {\firstargumenttrue
2678%    \doifelsenextoptional
2679%      {\syst_helpers_quintuple_empty_two_yes#1{#2}}%
2680%      {\syst_helpers_quintuple_empty_two_nop#1{#2}}}
2681%
2682% \def\syst_helpers_quintuple_empty_two_yes#1#2[#3]%
2683%   {\secondargumenttrue
2684%    \doifelsenextoptional
2685%      {\syst_helpers_quintuple_empty_three_yes#1{#2}{#3}}%
2686%      {\syst_helpers_quintuple_empty_three_nop#1{#2}{#3}}}
2687%
2688% \def\syst_helpers_quintuple_empty_three_yes#1#2#3[#4]%
2689%   {\thirdargumenttrue
2690%    \doifelsenextoptional
2691%      {\syst_helpers_quintuple_empty_four_yes#1{#2}{#3}{#4}}%
2692%      {\syst_helpers_quintuple_empty_four_nop#1{#2}{#3}{#4}}}
2693%
2694% \def\syst_helpers_quintuple_empty_four_yes#1#2#3#4[#5]%
2695%   {\fourthargumenttrue
2696%    \doifelsenextoptional
2697%      {\fifthargumenttrue#1[{#2}][{#3}][{#4}][{#5}]}%
2698%      {\syst_helpers_quintuple_empty_five_nop#1{#2}{#3}{#4}{#5}}}
2699%
2700% \def\syst_helpers_quintuple_empty_one_nop#1%
2701%   {\firstargumentfalse
2702%    \secondargumentfalse
2703%    \thirdargumentfalse
2704%    \fourthargumentfalse
2705%    \fifthargumentfalse
2706%    #1[][][][][]}
2707%
2708% \def\syst_helpers_quintuple_empty_two_nop
2709%   {\secondargumentfalse
2710%    \thirdargumentfalse
2711%    \fourthargumentfalse
2712%    \fifthargumentfalse
2713%    \if_next_blank_space_token
2714%      \expandafter\syst_helpers_quintuple_empty_two_spaced
2715%    \else
2716%      \expandafter\syst_helpers_quintuple_empty_two_normal
2717%    \fi}
2718%
2719% \def\syst_helpers_quintuple_empty_three_nop
2720%   {\thirdargumentfalse
2721%    \fourthargumentfalse
2722%    \fifthargumentfalse
2723%    \if_next_blank_space_token
2724%      \expandafter\syst_helpers_quintuple_empty_three_spaced
2725%    \else
2726%      \expandafter\syst_helpers_quintuple_empty_three_normal
2727%    \fi}
2728%
2729% \def\syst_helpers_quintuple_empty_four_nop
2730%   {\fourthargumentfalse
2731%    \fifthargumentfalse
2732%    \if_next_blank_space_token
2733%      \expandafter\syst_helpers_quintuple_empty_four_spaced
2734%    \else
2735%      \expandafter\syst_helpers_quintuple_empty_four_normal
2736%    \fi}
2737%
2738% \def\syst_helpers_quintuple_empty_five_nop
2739%   {\fifthargumentfalse
2740%    \if_next_blank_space_token
2741%      \expandafter\syst_helpers_quintuple_empty_five_spaced
2742%    \else
2743%      \expandafter\syst_helpers_quintuple_empty_five_normal
2744%    \fi}
2745%
2746% \def\syst_helpers_quintuple_empty_two_spaced        #1#2{#1[{#2}][][][][] }
2747% \def\syst_helpers_quintuple_empty_two_normal        #1#2{#1[{#2}][][][][]}
2748% \def\syst_helpers_quintuple_empty_three_spaced    #1#2#3{#1[{#2}][{#3}][][][] }
2749% \def\syst_helpers_quintuple_empty_three_normal    #1#2#3{#1[{#2}][{#3}][][][]}
2750% \def\syst_helpers_quintuple_empty_four_spaced   #1#2#3#4{#1[{#2}][{#3}][{#4}][][] }
2751% \def\syst_helpers_quintuple_empty_four_normal   #1#2#3#4{#1[{#2}][{#3}][{#4}][][]}
2752% \def\syst_helpers_quintuple_empty_five_spaced #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][] }
2753% \def\syst_helpers_quintuple_empty_five_normal #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][]}
2754
2755\protected\def\doquintupleempty#1%
2756  {%syst_helpers_argument_reset
2757   \t_syst_aux{#1}%
2758   \let\m_syst_action_yes\syst_helpers_quintuple_empty_one_yes
2759   \let\m_syst_action_nop\syst_helpers_quintuple_empty_one_nop
2760   \let\if_next_blank_space_token\iffalse
2761   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2762
2763\def\syst_helpers_quintuple_empty_one_yes[#1]%
2764  {\firstargumenttrue
2765   \toksapp\t_syst_aux{[{#1}]}%
2766   \let\m_syst_action_yes\syst_helpers_quintuple_empty_two_yes
2767   \let\m_syst_action_nop\syst_helpers_quintuple_empty_two_nop
2768   \let\if_next_blank_space_token\iffalse
2769   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2770
2771\def\syst_helpers_quintuple_empty_two_yes[#1]%
2772  {\secondargumenttrue
2773   \toksapp\t_syst_aux{[{#1}]}%
2774   \let\m_syst_action_yes\syst_helpers_quintuple_empty_three_yes
2775   \let\m_syst_action_nop\syst_helpers_quintuple_empty_three_nop
2776   \let\if_next_blank_space_token\iffalse
2777   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2778
2779\def\syst_helpers_quintuple_empty_three_yes[#1]%
2780  {\thirdargumenttrue
2781   \toksapp\t_syst_aux{[{#1}]}%
2782   \let\m_syst_action_yes\syst_helpers_quintuple_empty_four_yes
2783   \let\m_syst_action_nop\syst_helpers_quintuple_empty_four_nop
2784   \let\if_next_blank_space_token\iffalse
2785   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2786
2787\def\syst_helpers_quintuple_empty_four_yes[#1]%
2788  {\fourthargumenttrue
2789   \toksapp\t_syst_aux{[{#1}]}%
2790   \let\m_syst_action_yes\syst_helpers_quintuple_empty_five_yes
2791   \let\m_syst_action_nop\syst_helpers_quintuple_empty_five_nop
2792   \let\if_next_blank_space_token\iffalse
2793   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2794
2795\def\syst_helpers_quintuple_empty_one_nop
2796  {\firstargumentfalse
2797   \secondargumentfalse
2798   \thirdargumentfalse
2799   \fourthargumentfalse
2800   \fifthargumentfalse
2801   \the\t_syst_aux[][][][][]}
2802
2803\def\syst_helpers_quintuple_empty_two_nop
2804  {\secondargumentfalse
2805   \thirdargumentfalse
2806   \fourthargumentfalse
2807   \fifthargumentfalse
2808   \if_next_blank_space_token
2809     \expandafter\syst_helpers_empty_spaced_four
2810   \else
2811     \expandafter\syst_helpers_empty_normal_four
2812   \fi}
2813
2814\def\syst_helpers_quintuple_empty_three_nop
2815  {\thirdargumentfalse
2816   \fourthargumentfalse
2817   \fifthargumentfalse
2818   \if_next_blank_space_token
2819     \expandafter\syst_helpers_empty_spaced_three
2820   \else
2821     \expandafter\syst_helpers_empty_normal_three
2822   \fi}
2823
2824\def\syst_helpers_quintuple_empty_four_nop
2825  {\fourthargumentfalse
2826   \fifthargumentfalse
2827   \if_next_blank_space_token
2828     \expandafter\syst_helpers_empty_spaced_two
2829   \else
2830     \expandafter\syst_helpers_empty_normal_two
2831   \fi}
2832
2833\def\syst_helpers_quintuple_empty_five_nop
2834  {\fifthargumentfalse
2835   \if_next_blank_space_token
2836     \expandafter\syst_helpers_empty_spaced_one
2837   \else
2838     \expandafter\syst_helpers_empty_normal_one
2839   \fi}
2840
2841%D Sixtuple:
2842
2843% \protected\def\dosixtupleempty#1%
2844%   {\syst_helpers_argument_reset
2845%    \doifelsenextoptional
2846%      {\syst_helpers_sixtuple_empty_one_yes#1}
2847%      {\syst_helpers_sixtuple_empty_one_nop#1}}
2848%
2849% \def\syst_helpers_sixtuple_empty_one_yes#1[#2]%
2850%   {\firstargumenttrue
2851%    \doifelsenextoptional
2852%      {\syst_helpers_sixtuple_empty_two_yes#1{#2}}%
2853%      {\syst_helpers_sixtuple_empty_two_nop#1{#2}}}
2854%
2855% \def\syst_helpers_sixtuple_empty_two_yes#1#2[#3]%
2856%   {\secondargumenttrue
2857%    \doifelsenextoptional
2858%      {\syst_helpers_sixtuple_empty_three_yes#1{#2}{#3}}%
2859%      {\syst_helpers_sixtuple_empty_three_nop#1{#2}{#3}}}
2860%
2861% \def\syst_helpers_sixtuple_empty_three_yes#1#2#3[#4]%
2862%   {\thirdargumenttrue
2863%    \doifelsenextoptional
2864%      {\syst_helpers_sixtuple_empty_four_yes#1{#2}{#3}{#4}}%
2865%      {\syst_helpers_sixtuple_empty_four_nop#1{#2}{#3}{#4}}}
2866%
2867% \def\syst_helpers_sixtuple_empty_four_yes#1#2#3#4[#5]%
2868%   {\fourthargumenttrue
2869%    \doifelsenextoptional
2870%      {\syst_helpers_sixtuple_empty_five_yes#1{#2}{#3}{#4}{#5}}%
2871%      {\syst_helpers_sixtuple_empty_five_nop#1{#2}{#3}{#4}{#5}}}
2872%
2873% \def\syst_helpers_sixtuple_empty_five_yes#1#2#3#4#5[#6]%
2874%   {\fifthargumenttrue
2875%    \doifelsenextoptional
2876%      {\sixthargumenttrue#1[{#2}][{#3}][{#4}][{#5}][{#6}]}%
2877%      {\syst_helpers_sixtuple_empty_six_nop#1{#2}{#3}{#4}{#5}{#6}}}
2878%
2879% \def\syst_helpers_sixtuple_empty_one_nop#1%
2880%   {\firstargumentfalse
2881%    \secondargumentfalse
2882%    \thirdargumentfalse
2883%    \fourthargumentfalse
2884%    \fifthargumentfalse
2885%    \sixthargumentfalse
2886%    #1[][][][][][]}
2887%
2888% \def\syst_helpers_sixtuple_empty_two_nop
2889%   {\secondargumentfalse
2890%    \thirdargumentfalse
2891%    \fourthargumentfalse
2892%    \fifthargumentfalse
2893%    \sixthargumentfalse
2894%    \if_next_blank_space_token
2895%      \expandafter\syst_helpers_sixtuple_empty_two_spaced
2896%    \else
2897%      \expandafter\syst_helpers_sixtuple_empty_two_normal
2898%    \fi}
2899%
2900% \def\syst_helpers_sixtuple_empty_three_nop
2901%   {\thirdargumentfalse
2902%    \fourthargumentfalse
2903%    \fifthargumentfalse
2904%    \sixthargumentfalse
2905%    \if_next_blank_space_token
2906%      \expandafter\syst_helpers_sixtuple_empty_three_spaced
2907%    \else
2908%      \expandafter\syst_helpers_sixtuple_empty_three_normal
2909%    \fi}
2910%
2911% \def\syst_helpers_sixtuple_empty_four_nop
2912%   {\fourthargumentfalse
2913%    \fifthargumentfalse
2914%    \sixthargumentfalse
2915%    \if_next_blank_space_token
2916%      \expandafter\syst_helpers_sixtuple_empty_four_spaced
2917%    \else
2918%      \expandafter\syst_helpers_sixtuple_empty_four_normal
2919%    \fi}
2920%
2921% \def\syst_helpers_sixtuple_empty_five_nop
2922%   {\fifthargumentfalse
2923%    \sixthargumentfalse
2924%    \if_next_blank_space_token
2925%      \expandafter\syst_helpers_sixtuple_empty_five_spaced
2926%    \else
2927%      \expandafter\syst_helpers_sixtuple_empty_five_normal
2928%    \fi}
2929%
2930% \def\syst_helpers_sixtuple_empty_six_nop
2931%   {\sixthargumentfalse
2932%    \if_next_blank_space_token
2933%      \expandafter\syst_helpers_sixtuple_empty_six_spaced
2934%    \else
2935%      \expandafter\syst_helpers_sixtuple_empty_six_normal
2936%    \fi}
2937%
2938% \def\syst_helpers_sixtuple_empty_two_spaced          #1#2{#1[{#2}][][][][][] }
2939% \def\syst_helpers_sixtuple_empty_two_normal          #1#2{#1[{#2}][][][][][]}
2940% \def\syst_helpers_sixtuple_empty_three_spaced      #1#2#3{#1[{#2}][{#3}][][][][] }
2941% \def\syst_helpers_sixtuple_empty_three_normal      #1#2#3{#1[{#2}][{#3}][][][][]}
2942% \def\syst_helpers_sixtuple_empty_four_spaced     #1#2#3#4{#1[{#2}][{#3}][{#4}][][][] }
2943% \def\syst_helpers_sixtuple_empty_four_normal     #1#2#3#4{#1[{#2}][{#3}][{#4}][][][]}
2944% \def\syst_helpers_sixtuple_empty_five_spaced   #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][][] }
2945% \def\syst_helpers_sixtuple_empty_five_normal   #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][][]}
2946% \def\syst_helpers_sixtuple_empty_six_spaced  #1#2#3#4#5#6{#1[{#2}][{#3}][{#4}][{#5}][{#6}][] }
2947% \def\syst_helpers_sixtuple_empty_six_normal  #1#2#3#4#5#6{#1[{#2}][{#3}][{#4}][{#5}][{#6}][]}
2948
2949\protected\def\dosixtupleempty#1%
2950  {%syst_helpers_argument_reset
2951   \t_syst_aux{#1}%
2952   \let\m_syst_action_yes\syst_helpers_sixtuple_empty_one_yes
2953   \let\m_syst_action_nop\syst_helpers_sixtuple_empty_one_nop
2954   \let\if_next_blank_space_token\iffalse
2955   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2956
2957\def\syst_helpers_sixtuple_empty_one_yes[#1]%
2958  {\firstargumenttrue
2959   \toksapp\t_syst_aux{[{#1}]}%
2960   \let\m_syst_action_yes\syst_helpers_sixtuple_empty_two_yes
2961   \let\m_syst_action_nop\syst_helpers_sixtuple_empty_two_nop
2962   \let\if_next_blank_space_token\iffalse
2963   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2964
2965\def\syst_helpers_sixtuple_empty_two_yes[#1]%
2966  {\secondargumenttrue
2967   \toksapp\t_syst_aux{[{#1}]}%
2968   \let\m_syst_action_yes\syst_helpers_sixtuple_empty_three_yes
2969   \let\m_syst_action_nop\syst_helpers_sixtuple_empty_three_nop
2970   \let\if_next_blank_space_token\iffalse
2971   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2972
2973\def\syst_helpers_sixtuple_empty_three_yes[#1]%
2974  {\thirdargumenttrue
2975   \toksapp\t_syst_aux{[{#1}]}%
2976   \let\m_syst_action_yes\syst_helpers_sixtuple_empty_four_yes
2977   \let\m_syst_action_nop\syst_helpers_sixtuple_empty_four_nop
2978   \let\if_next_blank_space_token\iffalse
2979   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2980
2981\def\syst_helpers_sixtuple_empty_four_yes[#1]%
2982  {\fourthargumenttrue
2983   \toksapp\t_syst_aux{[{#1}]}%
2984   \let\m_syst_action_yes\syst_helpers_sixtuple_empty_five_yes
2985   \let\m_syst_action_nop\syst_helpers_sixtuple_empty_five_nop
2986   \let\if_next_blank_space_token\iffalse
2987   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2988
2989\def\syst_helpers_sixtuple_empty_five_yes[#1]%
2990  {\fifthargumenttrue
2991   \toksapp\t_syst_aux{[{#1}]}%
2992   \let\m_syst_action_yes\syst_helpers_sixtuple_empty_six_yes
2993   \let\m_syst_action_nop\syst_helpers_sixtuple_empty_six_nop
2994   \let\if_next_blank_space_token\iffalse
2995   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
2996
2997\def\syst_helpers_sixtuple_empty_one_nop
2998  {\firstargumentfalse
2999   \secondargumentfalse
3000   \thirdargumentfalse
3001   \fourthargumentfalse
3002   \fifthargumentfalse
3003   \sixthargumentfalse
3004   \the\t_syst_aux[][][][][][]}
3005
3006\def\syst_helpers_sixtuple_empty_two_nop
3007  {\secondargumentfalse
3008   \thirdargumentfalse
3009   \fourthargumentfalse
3010   \fifthargumentfalse
3011   \sixthargumentfalse
3012   \if_next_blank_space_token
3013     \expandafter\syst_helpers_empty_spaced_five
3014   \else
3015     \expandafter\syst_helpers_empty_normal_five
3016   \fi}
3017
3018\def\syst_helpers_sixtuple_empty_three_nop
3019  {\thirdargumentfalse
3020   \fourthargumentfalse
3021   \fifthargumentfalse
3022   \sixthargumentfalse
3023   \if_next_blank_space_token
3024     \expandafter\syst_helpers_empty_spaced_four
3025   \else
3026     \expandafter\syst_helpers_empty_normal_four
3027   \fi}
3028
3029\def\syst_helpers_sixtuple_empty_four_nop
3030  {\fourthargumentfalse
3031   \fifthargumentfalse
3032   \sixthargumentfalse
3033   \if_next_blank_space_token
3034     \expandafter\syst_helpers_empty_spaced_three
3035   \else
3036     \expandafter\syst_helpers_empty_normal_three
3037   \fi}
3038
3039\def\syst_helpers_sixtuple_empty_five_nop
3040  {\fifthargumentfalse
3041   \sixthargumentfalse
3042   \if_next_blank_space_token
3043     \expandafter\syst_helpers_empty_spaced_two
3044   \else
3045     \expandafter\syst_helpers_empty_normal_two
3046   \fi}
3047
3048\def\syst_helpers_sixtuple_empty_six_nop
3049  {\sixthargumentfalse
3050   \if_next_blank_space_token
3051     \expandafter\syst_helpers_empty_spaced_one
3052   \else
3053     \expandafter\syst_helpers_empty_normal_one
3054   \fi}
3055
3056%D Seventuple:
3057
3058% \protected\def\doseventupleempty#1%
3059%   {\syst_helpers_argument_reset
3060%    \doifelsenextoptional
3061%      {\syst_helpers_seventuple_empty_one_yes#1}%
3062%      {\syst_helpers_seventuple_empty_one_nop#1}}
3063%
3064% \def\syst_helpers_seventuple_empty_one_yes#1[#2]%
3065%   {\firstargumenttrue
3066%    \doifelsenextoptional
3067%      {\syst_helpers_seventuple_empty_two_yes#1{#2}}%
3068%      {\syst_helpers_seventuple_empty_two_nop#1{#2}}}
3069%
3070% \def\syst_helpers_seventuple_empty_two_yes#1#2[#3]%
3071%   {\secondargumenttrue
3072%    \doifelsenextoptional
3073%      {\syst_helpers_seventuple_empty_three_yes#1{#2}{#3}}%
3074%      {\syst_helpers_seventuple_empty_three_nop#1{#2}{#3}}}
3075%
3076% \def\syst_helpers_seventuple_empty_three_yes#1#2#3[#4]%
3077%   {\thirdargumenttrue
3078%    \doifelsenextoptional
3079%      {\syst_helpers_seventuple_empty_four_yes#1{#2}{#3}{#4}}%
3080%      {\syst_helpers_seventuple_empty_four_nop#1{#2}{#3}{#4}}}
3081%
3082% \def\syst_helpers_seventuple_empty_four_yes#1#2#3#4[#5]%
3083%   {\fourthargumenttrue
3084%    \doifelsenextoptional
3085%      {\syst_helpers_seventuple_empty_five_yes#1{#2}{#3}{#4}{#5}}%
3086%      {\syst_helpers_seventuple_empty_five_nop#1{#2}{#3}{#4}{#5}}}
3087%
3088% \def\syst_helpers_seventuple_empty_five_yes#1#2#3#4#5[#6]%
3089%   {\fifthargumenttrue
3090%    \doifelsenextoptional
3091%      {\syst_helpers_seventuple_empty_six_yes#1{#2}{#3}{#4}{#5}{#6}}%
3092%      {\syst_helpers_seventuple_empty_six_nop#1{#2}{#3}{#4}{#5}{#6}}}
3093%
3094% \def\syst_helpers_seventuple_empty_six_yes#1#2#3#4#5#6[#7]%
3095%   {\sixthargumenttrue
3096%    \doifelsenextoptional
3097%      {\seventhargumenttrue#1[{#2}][{#3}][{#4}][{#5}][{#6}][{#7}]}%
3098%      {\syst_helpers_seventuple_empty_seven_nop#1{#2}{#3}{#4}{#5}{#6}{#7}}}
3099%
3100% \def\syst_helpers_seventuple_empty_one_nop#1%
3101%   {\firstargumentfalse
3102%    \secondargumentfalse
3103%    \thirdargumentfalse
3104%    \fourthargumentfalse
3105%    \fifthargumentfalse
3106%    \sixthargumentfalse
3107%    \seventhargumentfalse
3108%    #1[][][][][][][]}
3109%
3110% \def\syst_helpers_seventuple_empty_two_nop
3111%   {\secondargumentfalse
3112%    \thirdargumentfalse
3113%    \fourthargumentfalse
3114%    \fifthargumentfalse
3115%    \sixthargumentfalse
3116%    \seventhargumentfalse
3117%    \if_next_blank_space_token
3118%      \expandafter\syst_helpers_seventuple_empty_two_spaced
3119%    \else
3120%      \expandafter\syst_helpers_seventuple_empty_two_normal
3121%    \fi}
3122%
3123% \def\syst_helpers_seventuple_empty_three_nop
3124%   {\thirdargumentfalse
3125%    \fourthargumentfalse
3126%    \fifthargumentfalse
3127%    \sixthargumentfalse
3128%    \seventhargumentfalse
3129%    \if_next_blank_space_token
3130%      \expandafter\syst_helpers_seventuple_empty_three_spaced
3131%    \else
3132%      \expandafter\syst_helpers_seventuple_empty_three_normal
3133%    \fi}
3134%
3135% \def\syst_helpers_seventuple_empty_four_nop
3136%   {\fourthargumentfalse
3137%    \fifthargumentfalse
3138%    \sixthargumentfalse
3139%    \seventhargumentfalse
3140%    \if_next_blank_space_token
3141%      \expandafter\syst_helpers_seventuple_empty_four_spaced
3142%    \else
3143%      \expandafter\syst_helpers_seventuple_empty_four_normal
3144%    \fi}
3145%
3146% \def\syst_helpers_seventuple_empty_five_nop
3147%   {\fifthargumentfalse
3148%    \sixthargumentfalse
3149%    \seventhargumentfalse
3150%    \if_next_blank_space_token
3151%      \expandafter\syst_helpers_seventuple_empty_five_spaced
3152%    \else
3153%      \expandafter\syst_helpers_seventuple_empty_five_normal
3154%    \fi}
3155%
3156% \def\syst_helpers_seventuple_empty_six_nop
3157%   {\sixthargumentfalse
3158%    \seventhargumentfalse
3159%    \if_next_blank_space_token
3160%      \expandafter\syst_helpers_seventuple_empty_six_spaced
3161%    \else
3162%      \expandafter\syst_helpers_seventuple_empty_six_normal
3163%    \fi}
3164%
3165% \def\syst_helpers_seventuple_empty_seven_nop
3166%   {\seventhargumentfalse
3167%    \if_next_blank_space_token
3168%      \expandafter\syst_helpers_seventuple_empty_seven_spaced
3169%    \else
3170%      \expandafter\syst_helpers_seventuple_empty_seven_normal
3171%    \fi}
3172%
3173% \def\syst_helpers_seventuple_empty_two_spaced            #1#2{#1[{#2}][][][][][][] }
3174% \def\syst_helpers_seventuple_empty_two_normal            #1#2{#1[{#2}][][][][][][]}
3175% \def\syst_helpers_seventuple_empty_three_spaced        #1#2#3{#1[{#2}][{#3}][][][][][] }
3176% \def\syst_helpers_seventuple_empty_three_normal        #1#2#3{#1[{#2}][{#3}][][][][][]}
3177% \def\syst_helpers_seventuple_empty_four_spaced       #1#2#3#4{#1[{#2}][{#3}][{#4}][][][][] }
3178% \def\syst_helpers_seventuple_empty_four_normal       #1#2#3#4{#1[{#2}][{#3}][{#4}][][][][]}
3179% \def\syst_helpers_seventuple_empty_five_spaced     #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][][][] }
3180% \def\syst_helpers_seventuple_empty_five_normal     #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][][][]}
3181% \def\syst_helpers_seventuple_empty_six_spaced    #1#2#3#4#5#6{#1[{#2}][{#3}][{#4}][{#5}][{#6}][][] }
3182% \def\syst_helpers_seventuple_empty_six_normal    #1#2#3#4#5#6{#1[{#2}][{#3}][{#4}][{#5}][{#6}][][]}
3183% \def\syst_helpers_seventuple_empty_seven_spaced#1#2#3#4#5#6#7{#1[{#2}][{#3}][{#4}][{#5}][{#6}][{#7}][] }
3184% \def\syst_helpers_seventuple_empty_seven_normal#1#2#3#4#5#6#7{#1[{#2}][{#3}][{#4}][{#5}][{#6}][{#7}][]}
3185
3186\protected\def\doseventupleempty#1%
3187  {%syst_helpers_argument_reset
3188   \t_syst_aux{#1}%
3189   \let\m_syst_action_yes\syst_helpers_seventuple_empty_one_yes
3190   \let\m_syst_action_nop\syst_helpers_seventuple_empty_one_nop
3191   \let\if_next_blank_space_token\iffalse
3192   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
3193
3194\def\syst_helpers_seventuple_empty_one_yes[#1]%
3195  {\firstargumenttrue
3196   \toksapp\t_syst_aux{[{#1}]}%
3197   \let\m_syst_action_yes\syst_helpers_seventuple_empty_two_yes
3198   \let\m_syst_action_nop\syst_helpers_seventuple_empty_two_nop
3199   \let\if_next_blank_space_token\iffalse
3200   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
3201
3202\def\syst_helpers_seventuple_empty_two_yes[#1]%
3203  {\secondargumenttrue
3204   \toksapp\t_syst_aux{[{#1}]}%
3205   \let\m_syst_action_yes\syst_helpers_seventuple_empty_three_yes
3206   \let\m_syst_action_nop\syst_helpers_seventuple_empty_three_nop
3207   \let\if_next_blank_space_token\iffalse
3208   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
3209
3210\def\syst_helpers_seventuple_empty_three_yes[#1]%
3211  {\thirdargumenttrue
3212   \toksapp\t_syst_aux{[{#1}]}%
3213   \let\m_syst_action_yes\syst_helpers_seventuple_empty_four_yes
3214   \let\m_syst_action_nop\syst_helpers_seventuple_empty_four_nop
3215   \let\if_next_blank_space_token\iffalse
3216   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
3217
3218\def\syst_helpers_seventuple_empty_four_yes[#1]%
3219  {\fourthargumenttrue
3220   \toksapp\t_syst_aux{[{#1}]}%
3221   \let\m_syst_action_yes\syst_helpers_seventuple_empty_five_yes
3222   \let\m_syst_action_nop\syst_helpers_seventuple_empty_five_nop
3223   \let\if_next_blank_space_token\iffalse
3224   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
3225
3226\def\syst_helpers_seventuple_empty_five_yes[#1]%
3227  {\fifthargumenttrue
3228   \toksapp\t_syst_aux{[{#1}]}%
3229   \let\m_syst_action_yes\syst_helpers_seventuple_empty_six_yes
3230   \let\m_syst_action_nop\syst_helpers_seventuple_empty_six_nop
3231   \let\if_next_blank_space_token\iffalse
3232   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
3233
3234\def\syst_helpers_seventuple_empty_six_yes[#1]%
3235  {\sixthargumenttrue
3236   \toksapp\t_syst_aux{[{#1}]}%
3237   \let\m_syst_action_yes\syst_helpers_seventuple_empty_seven_yes
3238   \let\m_syst_action_nop\syst_helpers_seventuple_empty_seven_nop
3239   \let\if_next_blank_space_token\iffalse
3240   \futurelet\nexttoken\syst_helpers_inspect_next_optional_character}
3241
3242\def\syst_helpers_seventuple_empty_one_nop
3243  {\firstargumentfalse
3244   \secondargumentfalse
3245   \thirdargumentfalse
3246   \fourthargumentfalse
3247   \fifthargumentfalse
3248   \sixthargumentfalse
3249   \seventhargumentfalse
3250   \the\t_syst_aux[][][][][][][]}
3251
3252\def\syst_helpers_seventuple_empty_two_nop
3253  {\secondargumentfalse
3254   \thirdargumentfalse
3255   \fourthargumentfalse
3256   \fifthargumentfalse
3257   \sixthargumentfalse
3258   \seventhargumentfalse
3259   \if_next_blank_space_token
3260     \expandafter\syst_helpers_empty_spaced_six
3261   \else
3262     \expandafter\syst_helpers_empty_normal_six
3263   \fi}
3264
3265\def\syst_helpers_seventuple_empty_three_nop
3266  {\thirdargumentfalse
3267   \fourthargumentfalse
3268   \fifthargumentfalse
3269   \sixthargumentfalse
3270   \seventhargumentfalse
3271   \if_next_blank_space_token
3272     \expandafter\syst_helpers_empty_spaced_five
3273   \else
3274     \expandafter\syst_helpers_empty_normal_five
3275   \fi}
3276
3277\def\syst_helpers_seventuple_empty_four_nop
3278  {\fourthargumentfalse
3279   \fifthargumentfalse
3280   \sixthargumentfalse
3281   \seventhargumentfalse
3282   \if_next_blank_space_token
3283     \expandafter\syst_helpers_empty_spaced_four
3284   \else
3285     \expandafter\syst_helpers_empty_normal_four
3286   \fi}
3287
3288\def\syst_helpers_seventuple_empty_five_nop
3289  {\fifthargumentfalse
3290   \sixthargumentfalse
3291   \seventhargumentfalse
3292   \if_next_blank_space_token
3293     \expandafter\syst_helpers_empty_spaced_three
3294   \else
3295     \expandafter\syst_helpers_empty_normal_three
3296   \fi}
3297
3298\def\syst_helpers_seventuple_empty_six_nop
3299  {\sixthargumentfalse
3300   \seventhargumentfalse
3301   \if_next_blank_space_token
3302     \expandafter\syst_helpers_empty_spaced_two
3303   \else
3304     \expandafter\syst_helpers_empty_normal_two
3305   \fi}
3306
3307\def\syst_helpers_seventuple_empty_seven_nop
3308  {\seventhargumentfalse
3309   \if_next_blank_space_token
3310     \expandafter\syst_helpers_empty_spaced_one
3311   \else
3312     \expandafter\syst_helpers_empty_normal_one
3313   \fi}
3314
3315%D Aliases:
3316
3317\let\dosingleargument    \dosingleempty
3318\let\dodoubleargument    \dodoubleempty
3319\let\dotripleargument    \dotripleempty
3320\let\doquadrupleargument \doquadrupleempty
3321\let\doquintupleargument \doquintupleempty
3322\let\dosixtupleargument  \dosixtupleempty
3323\let\doseventupleargument\doseventupleempty
3324
3325%D \macros
3326%D   {strippedcsname}
3327%D
3328%D The next macro can be very useful when using \type{\csname} like in:
3329%D
3330%D \starttyping
3331%D \csname if\strippedcsname\something\endcsname
3332%D \stoptyping
3333%D
3334%D This expands to \type{\ifsomething}.
3335
3336% \def\strippedcsname
3337%   {\expandafter\gobbleoneargument\string}
3338
3339\let\strippedcsname\csstring
3340
3341%D \macros
3342%D   {complexorsimple,complexorsimpleempty}
3343%D
3344%D Setups can be optional. A command expecting a setup is prefixed by \type
3345%D {\complex}, a command without one gets the prefix \type {\simple}. Commands like
3346%D this can be defined by:
3347%D
3348%D \starttyping
3349%D \complexorsimple\command
3350%D \stoptyping
3351%D
3352%D When \type{\command} is followed by a \type{[setup]}, then
3353%D
3354%D \starttyping
3355%D \complexcommand [setup]
3356%D \stoptyping
3357%D
3358%D executes, else we get
3359%D
3360%D \starttyping
3361%D \simplecommand
3362%D \stoptyping
3363%D
3364%D An alternative for \type{\complexorsimple} is:
3365%D
3366%D \starttyping
3367%D \complexorsimpleempty {command}
3368%D \stoptyping
3369%D
3370%D Depending on the presence of \type{[setup]}, this one leads to one of:
3371%D
3372%D \starttyping
3373%D \complexcommando [setup]
3374%D \complexcommando []
3375%D \stoptyping
3376%D
3377%D Many \CONTEXT\ commands started as complex or simple ones, but changed into more
3378%D versatile (more object oriented) ones using the \type {\get..argument} commands.
3379
3380\protected\def\complexorsimple#1%
3381  {% \relax % prevents lookahead, brrr
3382   \doifelsenextoptional
3383     {\firstargumenttrue \csname\s!complex\csstring#1\endcsname}
3384     {\firstargumentfalse\csname\s!simple \csstring#1\endcsname}}
3385
3386\protected\def\complexorsimpleempty#1%
3387  {% \relax % prevents lookahead, brrr
3388   \doifelsenextoptional
3389     {\firstargumenttrue \csname\s!complex\csstring#1\endcsname}
3390     {\firstargumentfalse\csname\s!complex\csstring#1\endcsname[]}}
3391
3392%D \macros
3393%D   {definecomplexorsimple,definecomplexorsimpleempty}
3394%D
3395%D The previous commands are used that often that we found it worthwile to offer two
3396%D more alternatives. Watch the build in protection.
3397
3398\protected\def\syst_helpers_complex_or_simple#1#2%
3399  {\doifelsenextoptional{\firstargumenttrue#1}{\firstargumentfalse#2}}
3400
3401\protected\def\syst_helpers_complex_or_simple_empty#1%
3402  {\doifelsenextoptional{\firstargumenttrue#1}{\firstargumentfalse#1[]}}
3403
3404\protected\def\definecomplexorsimple#1%
3405  {\protected\edef#1{\syst_helpers_complex_or_simple
3406     \expandafter\noexpand\csname\s!complex\csstring#1\endcsname
3407     \expandafter\noexpand\csname\s!simple \csstring#1\endcsname}}
3408
3409\protected\def\definecomplexorsimpleempty#1%
3410  {\protected\edef#1{\syst_helpers_complex_or_simple_empty
3411     \expandafter\noexpand\csname\s!complex\csstring#1\endcsname}}
3412
3413%D These commands are called as:
3414%D
3415%D \starttyping
3416%D \definecomplexorsimple\command
3417%D \stoptyping
3418%D
3419%D Of course, we must have available
3420%D
3421%D \starttyping
3422%D \def\complexcommand[#1]{...}
3423%D \def\simplecommand     {...}
3424%D \stoptyping
3425%D
3426%D Using this construction saves a few string now and then.
3427
3428%D \macros
3429%D   {dosinglegroupempty,dodoublegroupempty,dotriplegroupempty,
3430%D    doquadruplegroupempty, doquintuplegroupempty}
3431%D
3432%D We've already seen some commands that take care of
3433%D optional arguments between \type{[]}. The next two commands
3434%D handle the ones with \type{{}}. They are called as:
3435%D
3436%D \starttyping
3437%D \dosinglegroupempty    \ineedONEargument
3438%D \dodoublegroupempty    \ineedTWOarguments
3439%D \dotriplegroupempty    \ineedTHREEarguments
3440%D \doquadruplegroupempty \ineedFOURarguments
3441%D \doquintuplegroupempty \ineedFIVEarguments
3442%D \stoptyping
3443%D
3444%D We can add additional definitions later when we have defined \type {\appendtoks}.
3445
3446\newconditional\c_syst_helpers_permit_spaces_between_groups
3447
3448\protected\def    \permitspacesbetweengroups{\settrue \c_syst_helpers_permit_spaces_between_groups}
3449\protected\def\dontpermitspacesbetweengroups{\setfalse\c_syst_helpers_permit_spaces_between_groups}
3450
3451\dontpermitspacesbetweengroups
3452
3453%D We can avoid the nasty if handling in \type {syst-gen} by splitting the lot in
3454%D pieces so that we have no nested \type {\nextarguments} potentially being an
3455%D \type {conditional} token. Okay, these macros are not called that often but it
3456%D saves crap when tracing.
3457
3458\protected\def\syst_helpers_get_grouped_argument#1#2%
3459  {\let\syst_helpers_get_grouped_argument_yes#1%
3460   \let\syst_helpers_get_grouped_argument_nop#2%
3461   \futurelet\nextargument\syst_helpers_get_grouped_argument_indeed}
3462
3463\def\syst_helpers_get_grouped_argument_indeed
3464  {\ifx\nextargument\bgroup
3465     \expandafter\syst_helpers_get_grouped_argument_a
3466   \else
3467     \expandafter\syst_helpers_get_grouped_argument_b
3468   \fi}
3469
3470\def\syst_helpers_get_grouped_argument_a
3471  {%syst_helpers_argument_reset
3472   \syst_helpers_get_grouped_argument_yes\syst_helpers_get_grouped_argument_nested}
3473
3474\def\syst_helpers_get_grouped_argument_b
3475  {\ifconditional\c_syst_helpers_permit_spaces_between_groups
3476     \expandafter\syst_helpers_get_grouped_argument_f
3477   \else
3478     \expandafter\syst_helpers_get_grouped_argument_d
3479   \fi}
3480
3481\def\syst_helpers_get_grouped_argument_d
3482  {%syst_helpers_argument_error
3483   \syst_helpers_get_grouped_argument_nop\syst_helpers_get_grouped_argument_nested{}}
3484
3485\begingroup
3486  \def\\ {\syst_helpers_get_grouped_argument\syst_helpers_get_grouped_argument_yes\syst_helpers_get_grouped_argument_nop}
3487  \glet\syst_helpers_get_grouped_argument_e\\
3488\endgroup
3489
3490\def\syst_helpers_get_grouped_argument_f
3491  {\ifx\nextargument\blankspace
3492     \expandafter\syst_helpers_get_grouped_argument_e % g
3493   \else
3494     \expandafter\syst_helpers_get_grouped_argument_d % h
3495   \fi}
3496
3497\protected\def\dosinglegroupempty#1%
3498  {\def\syst_helpers_get_grouped_argument_nested
3499     {\dontpermitspacesbetweengroups
3500      #1}%
3501   \syst_helpers_get_grouped_argument\firstargumenttrue\firstargumentfalse}
3502
3503\protected\def\dodoublegroupempty#1%
3504  {\def\syst_helpers_get_grouped_argument_nested##1%
3505     {\def\syst_helpers_get_grouped_argument_nested
3506        {\dontpermitspacesbetweengroups
3507         #1{##1}}%
3508      \syst_helpers_get_grouped_argument\secondargumenttrue\secondargumentfalse}%
3509   \syst_helpers_get_grouped_argument\firstargumenttrue\firstargumentfalse}
3510
3511\protected\def\dotriplegroupempty#1%
3512  {\def\syst_helpers_get_grouped_argument_nested##1%
3513     {\def\syst_helpers_get_grouped_argument_nested####1%
3514        {\def\syst_helpers_get_grouped_argument_nested
3515           {\dontpermitspacesbetweengroups
3516            #1{##1}{####1}}%
3517         \syst_helpers_get_grouped_argument\thirdargumenttrue\thirdargumentfalse}%
3518      \syst_helpers_get_grouped_argument\secondargumenttrue\secondargumentfalse}%
3519   \syst_helpers_get_grouped_argument\firstargumenttrue\firstargumentfalse}
3520
3521\protected\def\doquadruplegroupempty#1%
3522  {\def\syst_helpers_get_grouped_argument_nested##1%
3523     {\def\syst_helpers_get_grouped_argument_nested####1%
3524        {\def\syst_helpers_get_grouped_argument_nested########1%
3525           {\def\syst_helpers_get_grouped_argument_nested
3526              {\dontpermitspacesbetweengroups
3527               #1{##1}{####1}{########1}}%
3528            \syst_helpers_get_grouped_argument\fourthargumenttrue\fourthargumentfalse}%
3529         \syst_helpers_get_grouped_argument\thirdargumenttrue\thirdargumentfalse}%
3530      \syst_helpers_get_grouped_argument\secondargumenttrue\secondargumentfalse}%
3531   \syst_helpers_get_grouped_argument\firstargumenttrue\firstargumentfalse}
3532
3533\protected\def\doquintuplegroupempty#1%
3534  {\def\syst_helpers_get_grouped_argument_nested##1%
3535     {\def\syst_helpers_get_grouped_argument_nested####1%
3536        {\def\syst_helpers_get_grouped_argument_nested########1%
3537           {\def\syst_helpers_get_grouped_argument_nested################1%
3538             {\def\syst_helpers_get_grouped_argument_nested
3539                {\dontpermitspacesbetweengroups
3540                 #1{##1}{####1}{########1}{################1}}%
3541              \syst_helpers_get_grouped_argument\fifthargumenttrue\fifthargumentfalse}%
3542            \syst_helpers_get_grouped_argument\fourthargumenttrue\fourthargumentfalse}%
3543         \syst_helpers_get_grouped_argument\thirdargumenttrue\thirdargumentfalse}%
3544      \syst_helpers_get_grouped_argument\secondargumenttrue\secondargumentfalse}%
3545   \syst_helpers_get_grouped_argument\firstargumenttrue\firstargumentfalse}
3546
3547%D These macros can explictly take care of spaces, which means that the next
3548%D definition and calls are valid:
3549%D
3550%D \starttyping
3551%D \def\test#1#2#3{[#1#2#3]}
3552%D
3553%D \dotriplegroupempty\test {a}{b}{c}
3554%D \dotriplegroupempty\test {a}{b}
3555%D \dotriplegroupempty\test {a}
3556%D \dotriplegroupempty\test
3557%D \dotriplegroupempty\test {a} {b} {c}
3558%D \dotriplegroupempty\test {a} {b}
3559%D \dotriplegroupempty\test
3560%D   {a}
3561%D   {b}
3562%D \stoptyping
3563%D
3564%D And alike.
3565
3566%D \macros
3567%D   {firstofoneargument, firstoftwoarguments, firstofthreearguments
3568%D    secondoftwoarguments, secondofthreearguments,
3569%D    thirdofthreearguments}
3570%D
3571%D The next six macros (dedicated to Taco) can conveniently used to select
3572%D arguments. Their names explain their functionality.
3573
3574\def\firstofoneargument            #1{#1}
3575
3576\def\firstoftwoarguments         #1#2{#1}
3577\def\secondoftwoarguments        #1#2{#2}
3578
3579\def\firstofthreearguments     #1#2#3{#1}
3580\def\secondofthreearguments    #1#2#3{#2}
3581\def\thirdofthreearguments     #1#2#3{#3}
3582
3583\def\firstoffourarguments    #1#2#3#4{#1}
3584\def\secondoffourarguments   #1#2#3#4{#2}
3585\def\thirdoffourarguments    #1#2#3#4{#3}
3586\def\fourthoffourarguments   #1#2#3#4{#4}
3587
3588\def\firstoffivearguments  #1#2#3#4#5{#1}
3589\def\secondoffivearguments #1#2#3#4#5{#2}
3590\def\thirdoffivearguments  #1#2#3#4#5{#3}
3591\def\fourthoffivearguments #1#2#3#4#5{#4}
3592\def\fifthoffivearguments  #1#2#3#4#5{#5}
3593
3594\def\firstofsixarguments #1#2#3#4#5#6{#1}
3595\def\secondofsixarguments#1#2#3#4#5#6{#2}
3596\def\thirdofsixarguments #1#2#3#4#5#6{#3}
3597\def\fourthofsixarguments#1#2#3#4#5#6{#4}
3598\def\fifthofsixarguments #1#2#3#4#5#6{#5}
3599\def\sixthofsixarguments #1#2#3#4#5#6{#6}
3600
3601\protected\def\firstofoneunexpanded       #1{#1}
3602
3603\protected\def\firstoftwounexpanded     #1#2{#1}
3604\protected\def\secondoftwounexpanded    #1#2{#2}
3605
3606\protected\def\firstofthreeunexpanded #1#2#3{#1}
3607\protected\def\secondofthreeunexpanded#1#2#3{#2}
3608\protected\def\thirdofthreeunexpanded #1#2#3{#3}
3609
3610%D \macros
3611%D   {globalletempty,letempty,
3612%D    letvalueempty,letgvalueempty,
3613%D    letvaluerelax,letgvaluerelax}
3614%D
3615%D Trivial:
3616
3617\protected\def\letempty      #1{\let #1\empty}
3618\protected\def\globalletempty#1{\glet#1\empty}
3619
3620\protected\def\letvalueempty #1{\expandafter\let \csname#1\endcsname\empty}
3621\protected\def\letgvalueempty#1{\expandafter\glet\csname#1\endcsname\empty}
3622\protected\def\letvaluerelax #1{\expandafter\let \csname#1\endcsname\relax}
3623\protected\def\letgvalurelax #1{\expandafter\glet\csname#1\endcsname\relax}
3624
3625\protected\def\relaxvalueifundefined#1%
3626  {\ifcsname#1\endcsname \else
3627     \expandafter\let\csname#1\endcsname\relax
3628   \fi}
3629
3630%D \macros
3631%D   {wait}
3632%D
3633%D The next macro hardly needs explanation. Because no nesting is to be expected, we
3634%D can reuse \type {\wait} within \type {\wait} itself.
3635
3636\protected\def\wait
3637  {\begingroup
3638   \read16 to \wait
3639   \endgroup}
3640
3641%D \macros
3642%D   {writestring,writeline,
3643%D    writestatus,statuswidth,normalwritestatus}
3644%D
3645%D Maybe one didn't notice, but we've already introduced a macro for showing
3646%D messages. In the multi||lingual modules, we will also introduce a mechanism for
3647%D message passing. For the moment we stick to the core macros:
3648%D
3649%D \starttyping
3650%D \writestring {string}
3651%D \writeline
3652%D \writestatus {category} {message}
3653%D \stoptyping
3654%D
3655%D Messages are formatted. One can provide the maximum with of the identification
3656%D string with the macro \type {\statuswidth}.
3657
3658\setnewconstant\statuswidth  15
3659\setnewconstant\statuswrite 128 % \pluscxxviii
3660
3661\ifdefined\writestring \else
3662
3663    \protected\def\writestring{\immediate\write\statuswrite}
3664    \protected\def\writeline  {\writestring{}}
3665
3666\fi
3667
3668\protected\def\normalwritestatus#1#2%
3669  {\writestring{\expandafter\syst_helpers_split_status_yes\expandafter\statuswidth#1%
3670     \space\space\space\space\space\space\space
3671     \space\space\space\space\space\space\space
3672     \space\space\space\space\space\space\end
3673     \space:\space#2}}
3674
3675\def\syst_helpers_split_status_yes#1#2%
3676  {\ifcase#1 \expandafter\syst_helpers_split_status_nop\fi#2%
3677   \expandafter\syst_helpers_split_status_yes\expandafter{\the\numexpr#1+\minusone\relax}}
3678
3679\def\syst_helpers_split_status_nop#1\end
3680  {}
3681
3682%D \macros
3683%D   {immediatemessage}
3684%D
3685%D A fully expandable message:
3686
3687\let\immediatemessage\clf_immediatemessage % {} mandate
3688
3689%D \macros
3690%D   {rawgetparameters}
3691%D
3692%D A raw and dirty alternative for \type {\getparameters}; no checking is done!
3693
3694\protected\def\rawsetparameter#1=#2,%
3695  {\if]#1\else
3696     \expandafter\def\csname\rawparameterprefix#1\endcsname{#2}%
3697     \expandafter\rawsetparameter
3698   \fi}
3699
3700\protected\def\rawgetparameters[#1][#2% some 5-10% faster
3701  {\ifx#2]% test is needed, else bomb on [#1][]
3702     \expandafter\gobbleoneargument
3703   \else
3704     \def\rawparameterprefix{#1}%
3705     \expandafter\dorawgetparameters
3706   \fi#2}
3707
3708\def\dorawgetparameters#1]%
3709  {\expandafter\rawsetparameter#1,]=,}
3710
3711%D \macros
3712%D   {doglobal,
3713%D    redoglobal,dodoglobal,resetglobal}
3714%D
3715%D The two macros \type {\redoglobal} and \type{\dodoglobal} are used in this and
3716%D some other modules to enforce a user specified \type {\doglobal} action. The last
3717%D and often only global assignment in a macro is done with \type {\dodoglobal}, but
3718%D all preceding ones with \type {\redoglobal}. When using only alternatives, one
3719%D can reset this mechanism with \type {\resetglobal}.
3720
3721\protected\def\resetglobal
3722  {\let\redoglobal\relax
3723   \let\dodoglobal\relax}
3724
3725\resetglobal
3726
3727\protected\def\doglobal
3728  {\ifx\redoglobal\relax
3729     \let\redoglobal\global
3730     \let\dodoglobal\syst_helpers_dodo_global
3731   \fi}
3732
3733\def\syst_helpers_dodo_global
3734  {\resetglobal\global}
3735
3736\def\saveglobal
3737  {\let\syst_helpers_dodo_global\dodoglobal
3738   \let\syst_helpers_redo_global\redoglobal}
3739
3740\def\restoreglobal
3741  {\let\redoglobal\syst_helpers_redo_global
3742   \let\dodoglobal\syst_helpers_dodo_global}
3743
3744%D A very useful application of this macro is \type {\newif}, \TEX's fake boolean
3745%D type. Not being a primitive, \type {\global} hopelessly fails here. But a slight
3746%D adaption of Knuth's original macro permits:
3747%D
3748%D \starttyping
3749%D \doglobal\newif\iftest
3750%D \stoptyping
3751%D
3752%D Of course one can still say:
3753%D
3754%D \starttyping
3755%D \global\testtrue
3756%D \global\testfalse
3757%D \stoptyping
3758%D
3759%D Apart from the prefixes, a few more \type {\expandafters} are needed:
3760
3761% \protected\def\newif#1% uses the original plain \@if
3762%   {\privatescratchcounter\escapechar
3763%    \escapechar\minusone
3764%    \expandafter\expandafter\expandafter
3765%      \redoglobal\expandafter\expandafter\expandafter
3766%        \edef\@if#1{true}{\let\noexpand#1\noexpand\iftrue}%
3767%    \expandafter\expandafter\expandafter
3768%      \redoglobal\expandafter\expandafter\expandafter
3769%        \edef\@if#1{false}{\let\noexpand#1\noexpand\iffalse}%
3770%    \dodoglobal\@if#1{false}%
3771%    \escapechar\privatescratchcounter}
3772
3773\protected\def\newif#1% see syst-ini.mkiv
3774  {\let\new_if_saved\newif
3775   \let\newif\new_if_check
3776   \expandafter\redoglobal\expandafter\def\csname\expandafter\newif\csstring#1true\endcsname {\let#1\iftrue }%
3777   \expandafter\redoglobal\expandafter\def\csname\expandafter\newif\csstring#1false\endcsname{\let#1\iffalse}%
3778   \dodoglobal\csname\expandafter\newif\csstring#1false\endcsname
3779   \let\newif\new_if_saved}
3780
3781%D Also new:
3782
3783\protected\def\define#1%
3784  {\ifdefined#1%
3785     \message{[\noexpand#1is already defined]}%
3786     \protected\expandafter\def\expandafter\gobbleddefinition
3787   \else
3788     \protected\expandafter\def
3789   \fi#1}
3790
3791\protected\def\redefine#1%
3792  {\ifdefined#1%
3793     \message{[\noexpand#1is redefined]}%
3794   \fi
3795   \protected\def#1}
3796
3797\protected\def\definemacro#1%
3798  {\ifdefined#1%
3799     \message{[\noexpand#1is already defined]}%
3800     \protected\expandafter\def\expandafter\gobbleddefinition
3801   \else
3802     \protected\expandafter\def
3803   \fi#1}
3804
3805% \define\hans{hans}
3806% \redefine\hans{hans}
3807% \define\hans#1[]#2#3{hans}
3808
3809%D The next variant fits nicely in the setups syntax:
3810%D
3811%D \starttyping
3812%D \starttexdefinition bagger [#1] #2
3813%D     oeps
3814%D     #1
3815%D     oeps
3816%D \stoptexdefinition
3817%D
3818%D \bagger [a] {b}
3819%D \stoptyping
3820
3821% \starttexdefinition test
3822% oeps
3823% \stoptexdefinition
3824%
3825% [\test]
3826
3827\def\s!unexpanded{unexpanded}
3828
3829% \bgroup \obeylines
3830%
3831% \glet\stoptexdefinition\relax
3832%
3833% \protected\gdef\starttexdefinition%
3834%   {\bgroup%
3835%    \obeylines%
3836%    \syst_helpers_start_tex_definition}
3837%
3838% \gdef\syst_helpers_start_tex_definition #1
3839%   {\catcode\endoflineasciicode\ignorecatcode%
3840%    \doifinstringelse\letterhash{\detokenize{#1}}\syst_helpers_start_tex_definition_yes\syst_helpers_start_tex_definition_nop#1
3841%   }
3842%
3843% \gdef\syst_helpers_start_tex_definition_yes#1 #2
3844%   {\edef\texdefinitionname{#1}%
3845%    \ifx\texdefinitionname\s!unexpanded%
3846%      \expandafter\syst_helpers_start_tex_definition_yes_unexpanded%
3847%    \else%
3848%      \expandafter\syst_helpers_start_tex_definition_yes_normal%
3849%    \fi%
3850%    {#1}#2
3851%    }
3852%
3853% \gdef\syst_helpers_start_tex_definition_yes_unexpanded#1#2 #3
3854%                                        #4\stoptexdefinition%
3855%   {\egroup% #1=unexpanded
3856%    \protected\expandafter\def\csname#2\endcsname#3{#4}}
3857%
3858% \gdef\syst_helpers_start_tex_definition_yes_normal#1#2
3859%                                     #3\stoptexdefinition%
3860%   {\egroup%
3861%    \expandafter\def\csname#1\endcsname#2{#3}}
3862%
3863% \gdef\syst_helpers_start_tex_definition_nop#1
3864%   {\syst_helpers_start_tex_definition_nop_indeed{#1}{}}
3865%
3866% \gdef\syst_helpers_start_tex_definition_nop_indeed#1#2#3\stoptexdefinition%
3867%   {\egroup%
3868%    \expandafter\def\csname#1\endcsname{#3}}
3869%
3870% \egroup
3871
3872% \starttexdefinition unexpanded test #1
3873%     [here #1]
3874% \stoptexdefinition
3875%
3876% \starttexdefinition global unexpanded test
3877%     [here test]
3878% \stoptexdefinition
3879%
3880% \scratchcounter=123
3881%
3882% \starttexdefinition global unexpanded expanded test #oeps
3883%     [here #oeps: \the\scratchcounter]
3884% \stoptexdefinition
3885
3886% \bgroup \obeylines
3887%
3888% \glet\stoptexdefinition\relax
3889%
3890% \protected\gdef\starttexdefinition%
3891%   {\bgroup%
3892%    \obeylines%
3893%    \syst_helpers_start_tex_definition_one}
3894%
3895% \gdef\syst_helpers_start_tex_definition_one#1
3896%   {\catcode\endoflineasciicode\ignorecatcode%
3897%    \syst_helpers_start_tex_definition_two{#1}}
3898%
3899% \gdef\syst_helpers_start_tex_definition_two#1#2\stoptexdefinition%
3900%   {\egroup%
3901%    \ctxcommand{thetexdefinition("#1")}{#2}}
3902%
3903% \egroup
3904
3905\bgroup \obeylines
3906
3907\glet\stoptexdefinition\relax
3908
3909\protected\gdef\starttexdefinition%
3910  {\bgroup%
3911   \obeylines%
3912   \syst_helpers_start_tex_definition}
3913
3914\gdef\syst_helpers_start_tex_definition#1
3915  {\catcode\endoflineasciicode\ignorecatcode%
3916   \clf_texdefinition_one{#1}}
3917
3918\gdef\dostarttexdefinition#1\stoptexdefinition%
3919  {\egroup%
3920   \clf_texdefinition_two{#1}}
3921
3922\egroup
3923
3924% \protected\def\texdefinition#1{\csname\ifcsname#1\endcsname#1\else donothing\fi\endcsname} % todo: a nop cs: char 0 or some corenamespace
3925
3926\protected\def\texdefinition#1{\begincsname#1\endcsname}
3927
3928% This is a first variant, more might be added:
3929
3930\protected\def\starttexcode{\unprotect}
3931\protected\def\stoptexcode {\protect}
3932
3933%D \macros
3934%D   {newcounter,
3935%D    increment,decrement}
3936%D
3937%D Unfortunately the number of \COUNTERS\ in \TEX\ is limited, but fortunately we
3938%D can store numbers in a macro. We can increment such pseudo \COUNTERS\ with \type
3939%D {\increment}.
3940%D
3941%D \starttyping
3942%D \increment(\counter,20)
3943%D \increment(\counter,-4)
3944%D \increment(\counter)
3945%D \increment\counter
3946%D \stoptyping
3947%D
3948%D After this sequence of commands, the value of \type {\counter} is 20, 16, 17
3949%D and~18. Of course there is also the complementary command \type {\decrement}.
3950%D
3951%D Global assignments are possible too, using \type{\doglobal}:
3952%D
3953%D \starttyping
3954%D \doglobal\increment\counter
3955%D \stoptyping
3956%D
3957%D When \type {\counter} is undefined, it's value is initialized at~0. It is
3958%D nevertheless better to define a \COUNTER\ explicitly. One reason could be that
3959%D the \COUNTER\ can be part of a test with \type {\ifnum} and this conditional does
3960%D not accept undefined macro's. The \COUNTER\ in our example can for instance be
3961%D defined with:
3962%D
3963%D \starttyping
3964%D \newcounter\counter
3965%D \stoptyping
3966%D
3967%D The command \type {\newcounter} must not be confused with \type {\newcount}! Of
3968%D course this mechanism is much slower than using \TEX's \COUNTERS\ directly. In
3969%D practice \COUNTERS\ (and therefore our pseudo counters too) are seldom the
3970%D bottleneck in the processing of a text. Apart from some other incompatilities we
3971%D want to mention a pitfal when using \type {\ifnum}.
3972%D
3973%D \starttyping
3974%D \ifnum\normalcounter=\pseudocounter \doif \else \doelse \fi
3975%D \ifnum\pseudocounter=\normalcounter \doif \else \doelse \fi
3976%D \stoptyping
3977%D
3978%D In the first test, \TEX\ continues it's search for the second number after
3979%D reading \type {\pseudocounter}, while in the second test, it stops reading after
3980%D having encountered a real one. Tests like the first one therefore can give
3981%D unexpected results, for instance execution of \type {\doif} even if both numbers
3982%D are unequal.
3983
3984\def\zerocountervalue{0}
3985
3986\protected\def\newcounter#1%
3987  {\dodoglobal\let#1\zerocountervalue}
3988
3989%D Nowadays we don't mind a few more tokens if we can gain a bit of speed.
3990
3991\def\syst_helpers_do_increment#1{\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi+\plusone \relax}}
3992\def\syst_helpers_do_decrement#1{\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi+\minusone\relax}}
3993
3994\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}}
3995\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}}
3996
3997\def\syst_helpers_do_do_increment(#1%
3998  {\def\m_syst_action_yes{\syst_helpers_do_do_do_increment#1}%
3999   \def\m_syst_action_nop{\syst_helpers_do_do_do_increment#1,\plusone}%
4000   \doifelsenextcharcs,\m_syst_action_yes\m_syst_action_nop}
4001
4002\def\syst_helpers_do_do_decrement(#1%
4003  {\def\m_syst_action_yes{\syst_helpers_do_do_do_decrement#1}%
4004   \def\m_syst_action_nop{\syst_helpers_do_do_do_decrement#1,\plusone}%
4005   \doifelsenextcharcs,\m_syst_action_yes\m_syst_action_nop}
4006
4007\protected\def\increment{\doifelsenextcharcs(\syst_helpers_do_do_increment\syst_helpers_do_increment}
4008\protected\def\decrement{\doifelsenextcharcs(\syst_helpers_do_do_decrement\syst_helpers_do_decrement}
4009
4010\protected\def\fastincrement#1{\dodoglobal\edef#1{\the\numexpr#1+\plusone \relax}}
4011\protected\def\fastdecrement#1{\dodoglobal\edef#1{\the\numexpr#1+\minusone\relax}}
4012
4013\protected\def\incrementvalue#1{\expandafter\increment\csname#1\endcsname}
4014\protected\def\decrementvalue#1{\expandafter\decrement\csname#1\endcsname}
4015
4016%D \macros
4017%D  {newsignal}
4018%D
4019%D When writing advanced macros, we cannot do without signaling. A signal is a small
4020%D (invisible) kern or penalty that signals the next macro that something just
4021%D happened. This macro can take any action depending on the previous signal.
4022%D Signals must be unique and the next macro takes care of that.
4023%D
4024%D \starttyping
4025%D \newsignal\somesignal
4026%D \stoptyping
4027%D
4028%D Signals old dimensions and can be used in skips, kerns and tests like \type
4029%D {\ifdim}.
4030
4031\newdimen\maximumsignal % step is about 0.00025pt
4032
4033\protected\def\newsignal#1%
4034  {\ifdefined#1\else
4035     \advance\maximumsignal 2\scaledpoint % to be save in rounding
4036     \edef#1{\the\maximumsignal}%
4037   \fi}
4038
4039%D \macros
4040%D   {strippedcsname}
4041%D
4042%D The next macro can be very useful when using \type {\csname} like in:
4043%D
4044%D \starttyping
4045%D \csname if\strippedcsname\something\endcsname
4046%D \stoptyping
4047
4048% \def\checkedstrippedcsname#1% this permits \strippedcsname{\xxx} and \strippedcsname{xxx}
4049%   {\expandafter\syst_helpers_checked_stripped_csname\string#1}
4050%
4051% \def\syst_helpers_checked_stripped_csname#1%
4052%  %{\ifx#1\letterbackslash\else#1\fi}
4053%   {\if\noexpand#1\letterbackslash\else#1\fi}
4054
4055\let\checkedstrippedcsname\csstring
4056
4057%D \macros
4058%D   {savenormalmeaning}
4059%D
4060%D We will use this one in:
4061
4062\protected\def\savenormalmeaning#1%
4063  {\ifcsname normal\csstring#1\endcsname \else
4064     \expandafter\let\csname normal\csstring#1\endcsname#1%
4065   \fi}
4066
4067%D \macros
4068%D   {dorecurse,recurselevel,recursedepth,
4069%D    dostepwiserecurse}
4070%D
4071%D \TEX\ does not offer us powerfull for||loop mechanisms. On the other hand its
4072%D recursion engine is quite unique. We therefore identify the for||looping macros
4073%D by this method. The most simple alternative is the one that only needs a number.
4074%D
4075%D \starttyping
4076%D \dorecurse {n} {whatever we want}
4077%D \stoptyping
4078%D
4079%D This macro can be nested without problems and therefore be used in situations
4080%D where \PLAIN\ \TEX's \type {\loop} macro ungracefully fails. The current value of
4081%D the counter is available in \type {\recurselevel}, before as well as after the
4082%D \typ {whatever we wat} stuff.
4083%D
4084%D \starttyping
4085%D \dorecurse               % inner loop
4086%D   {10}
4087%D   {\recurselevel:          % outer value
4088%D      \dorecurse          % inner loop
4089%D        {\recurselevel}     % outer value
4090%D        {\recurselevel}     % inner value
4091%D      \dorecurse          % inner loop
4092%D        {\recurselevel}     % outer value
4093%D        {\recurselevel}     % inner value
4094%D    \endgraf}
4095%D \stoptyping
4096%D
4097%D In this example the first, second and fourth \type {\recurselevel} concern the
4098%D outer loop, while the third and fifth one concern the inner loop. The depth of
4099%D the nesting is available for inspection in \type {\recursedepth}.
4100%D
4101%D Both \type {\recurselevel} and \type {\recursedepth} are macros. The real
4102%D \COUNTERS\ are hidden from the user because we don't want any interference.
4103
4104\newcount\outerrecurse
4105\newcount\innerrecurse
4106
4107\def\recursedepth{\the\outerrecurse}
4108\def\recurselevel{0}
4109
4110\let\syst_helpers_stepwise_next\relax
4111
4112\installsystemnamespace{recurseindex}
4113\installsystemnamespace{recurseaction}
4114
4115\protected\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4
4116  {\global\advance\outerrecurse \plusone
4117   \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname{#4}%
4118   \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel
4119   \ifnum#3>\zerocount\relax
4120     \ifnum#2<#1\relax
4121       \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit
4122     \else
4123       \let\syst_helpers_stepwise_next\syst_helpers_stepwise_recurse
4124     \fi
4125   \else
4126     \ifnum#3<\zerocount\relax
4127       \ifnum#1<#2\relax
4128         \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit
4129       \else
4130         \let\syst_helpers_stepwise_next\syst_helpers_stepwise_reverse
4131       \fi
4132     \else
4133       \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit
4134     \fi
4135   \fi\normalexpanded{\syst_helpers_stepwise_next{\number#1}{\number#2}{\number#3}}}
4136
4137\protected\def\syst_helpers_stepwise_recurse#1#2#3% from to step
4138  {\ifnum#1>#2\relax
4139     \expandafter\syst_helpers_stepwise_recurse_nop
4140   \else
4141     \def\recurselevel{#1}%
4142     \doubleexpandafter\syst_helpers_stepwise_recurse_yes\expandafter
4143   \fi\expandafter{\the\numexpr\recurselevel+#3\relax}{#2}{#3}}
4144
4145\protected\def\syst_helpers_recurse_content
4146  {\csname\??recurseaction\the\outerrecurse\endcsname}
4147
4148\protected\def\syst_helpers_stepwise_recurse_yes
4149  {\syst_helpers_recurse_content
4150   \syst_helpers_stepwise_recurse}
4151
4152\protected\def\syst_helpers_stepwise_reverse#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     \innerrecurse#1\relax
4158     \advance\innerrecurse#3\relax
4159     \doubleexpandafter\syst_helpers_stepwise_reverse_yes\expandafter
4160   \fi\expandafter{\the\innerrecurse}{#2}{#3}}
4161
4162\protected\def\syst_helpers_stepwise_reverse_yes
4163  {\syst_helpers_recurse_content
4164   \syst_helpers_stepwise_reverse}
4165
4166\protected\def\syst_helpers_stepwise_exit
4167  {\syst_helpers_stepwise_recurse_nop\relax}
4168
4169\protected\def\syst_helpers_stepwise_recurse_nop#1#2#3#4%
4170  {\expandafter\let\expandafter\recurselevel\csname\??recurseindex\the\outerrecurse\endcsname
4171   \global\advance\outerrecurse\minusone}
4172
4173% \protected\def\nonostepwiserecurse#1#2#3%
4174%   {\expandafter\let\expandafter\recurselevel\csname\??recurseindex\the\outerrecurse\endcsname
4175%    \global\advance\outerrecurse\minusone}
4176
4177\protected\def\dorecurse#1%
4178  {\dostepwiserecurse\plusone{#1}\plusone}
4179
4180\def\doexpandedrecurse#1#2% user macro (also was \doxprecurse)
4181  {\ifnum#1>\zerocount
4182     #2\expandafter\doexpandedrecurse\expandafter{\the\numexpr#1-\plusone\relax}{#2}%
4183   \fi}
4184
4185%D As we can see here, the simple command \type{\dorecurse} is a special case of the
4186%D more general:
4187%D
4188%D \starttyping
4189%D \dostepwiserecurse {from} {to} {step} {action}
4190%D \stoptyping
4191%D
4192%D This commands accepts positive and negative steps. Illegal values are handles as
4193%D good as possible and the macro accepts numbers and \COUNTERS.
4194%D
4195%D \starttyping
4196%D \dostepwiserecurse  {1} {10}  {2} {...}
4197%D \dostepwiserecurse {10}  {1} {-2} {...}
4198%D \stoptyping
4199%D
4200%D Because the simple case is used often, we implement it more efficiently:
4201
4202\protected\def\dorecurse#1%
4203  {\ifcase#1\relax
4204     \expandafter\gobbletwoarguments
4205   \or
4206     \expandafter\syst_helpers_recurse_y
4207   \else
4208     \expandafter\syst_helpers_recurse_x
4209   \fi{#1}}
4210
4211\protected\def\syst_helpers_recurse_x#1#2%
4212  {\global\advance\outerrecurse \plusone
4213   \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname{#2}%
4214   \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel
4215   \expandafter\syst_helpers_recurse_indeed\expandafter1\expandafter{\number#1}}
4216
4217\protected\def\syst_helpers_recurse_y#1#2%
4218  {\global\advance\outerrecurse \plusone
4219   \expandafter\glet\csname\??recurseindex\the\outerrecurse\endcsname\recurselevel
4220   \let\recurselevel\!!plusone
4221   #2%
4222   \expandafter\let\expandafter\recurselevel\csname\??recurseindex\the\outerrecurse\endcsname
4223   \global\advance\outerrecurse \minusone}
4224
4225\protected\def\syst_helpers_recurse_indeed#1#2% from to
4226  {\ifnum#1>#2\relax
4227     \expandafter\syst_helpers_recurse_indeed_nop
4228   \else
4229     \def\recurselevel{#1}%
4230     \doubleexpandafter\syst_helpers_recurse_indeed_yes
4231   \fi\expandafter{\the\numexpr\recurselevel+\plusone\relax}{#2}}
4232
4233\protected\def\syst_helpers_recurse_indeed#1#2% from to
4234  {\ifnum#1>#2\relax
4235     \expandafter\syst_helpers_recurse_indeed_nop
4236   \else
4237     \def\recurselevel{#1}%
4238     \innerrecurse#1\advance\innerrecurse\plusone
4239     \doubleexpandafter\syst_helpers_recurse_indeed_yes
4240   \fi\expandafter{\the\innerrecurse}{#2}}
4241
4242\protected\def\syst_helpers_recurse_indeed_yes
4243  {\syst_helpers_recurse_content
4244   \syst_helpers_recurse_indeed}
4245
4246\protected\def\syst_helpers_recurse_indeed_nop#1#2#3%
4247  {\expandafter\let\expandafter\recurselevel\csname\??recurseindex\the\outerrecurse\endcsname
4248   \global\advance\outerrecurse \minusone }
4249
4250%D \macros
4251%D   {dowith}
4252%D
4253%D Here's a loop over whatever is in a list:
4254%D
4255%D \starttyping
4256%D \dowith{a,b,c}{[#1]}
4257%D \stoptyping
4258
4259\protected\def\dowith#1#2%
4260  {\def\syst_helpers_with##1{#2}%
4261   \normalexpanded{\processcommalist[#1]}\syst_helpers_with}
4262
4263%D \macros
4264%D   {doloop,exitloop}
4265%D
4266%D Sometimes loops are not determined by counters, but by (a combinations of)
4267%D conditions. We therefore implement a straightforward loop, which can only be left
4268%D when we explictly exit it. Nesting is supported. First we present a more
4269%D extensive alternative.
4270%D
4271%D \starttyping
4272%D \doloop
4273%D   {Some kind of typesetting punishment \par
4274%D    \ifnum\pageno>100 \exitloop \fi}
4275%D \stoptyping
4276%D
4277%D When needed, one can call for \type {\looplevel} and \type {\loopdepth}.
4278
4279\let\endofloop\donothing % maybe \syst_helpers_loop_end
4280
4281\protected\def\doloop#1%
4282  {\global\advance\outerrecurse \plusone
4283   \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname{#1}%
4284   \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel
4285   \let\endofloop\syst_helpers_loop
4286   \syst_helpers_loop1} % no \plusone else \recurselevel wrong
4287
4288\protected\def\syst_helpers_loop#1%
4289  {\def\recurselevel{#1}%
4290   \expandafter\syst_helpers_loop_yes\expandafter{\the\numexpr\recurselevel+\plusone\relax}}
4291
4292\protected\def\syst_helpers_loop_yes
4293  {\syst_helpers_recurse_content
4294   \endofloop}
4295
4296\protected\def\syst_helpers_loop_nop#1%
4297  {\let\endofloop\syst_helpers_loop % new, permits nested \doloop's
4298   \expandafter\let\expandafter\recurselevel\csname\??recurseindex\the\outerrecurse\endcsname
4299   \global\advance\outerrecurse\minusone}
4300
4301\protected\def\exitloop                     % \exitloop quits at end
4302  {\let\endofloop\syst_helpers_loop_nop}
4303
4304\protected\def\exitloopnow#1\endofloop % \exitloopnow quits directly
4305  {\syst_helpers_loop_nop}
4306
4307%D The loop is executed at least once, so beware of situations
4308%D like:
4309%D
4310%D \starttyping
4311%D \doloop {\exitloop some commands}
4312%D \stoptyping
4313%D
4314%D It's just a matter of putting the text into the \type {\if} statement that should
4315%D be there anyway, like in:
4316%D
4317%D \starttyping
4318%D \doloop {\ifwhatever \exitloop \else some commands\fi}
4319%D \stoptyping
4320%D
4321%D You can also quit a loop immediately, by using \type
4322%D {\exitloopnow} instead. Beware, this is more sensitive
4323%D for conditional errors.
4324
4325%D Krzysztof Leszczynski suggested to provide access to the level by means of a
4326%D \type {#1}. I decided to pass the more frequently used level as \type {#1} and
4327%D the less favoured depth as \type {#2}. The intended usage is:
4328%D
4329%D \starttyping
4330%D \dorecurse{3}{\definesymbol[test-#1][xx-#1]}
4331%D
4332%D \def\test{\dorecurse{3}{\definesymbol[test-##1][xx-##1]}} \test
4333%D
4334%D \symbol[test-1]\quad\symbol[test-2]\quad\symbol[test-3]
4335%D \stoptyping
4336%D
4337%D Since the hashed arguments are expanded, we don't need tricky expansion here.
4338%D
4339%D \starttyping
4340%D \dorecurse{3}{\expanded{\definesymbol[test-\recurselevel][xx-\recurselevel]}}
4341%D \stoptyping
4342
4343\def\syst_helpers_recurse_content
4344  {\csname\??recurseaction\the\outerrecurse\expandafter\expandafter\expandafter\endcsname
4345     \expandafter\expandafter\expandafter{\expandafter\recurselevel\expandafter}\expandafter{\the\outerrecurse}}
4346
4347\protected\def\syst_helpers_recurse_x#1#2%
4348  {\global\advance\outerrecurse \plusone
4349   \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#2}%
4350   \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel
4351   \expandafter\syst_helpers_recurse_indeed\expandafter1\expandafter{\number#1}}
4352
4353\protected\def\syst_helpers_recurse_y#1#2%
4354  {\global\advance\outerrecurse \plusone
4355   \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel
4356   \let\recurselevel\!!plusone
4357   \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#2}%
4358   \syst_helpers_recurse_content
4359   \expandafter\let\expandafter\recurselevel\csname\??recurseindex\the\outerrecurse\endcsname
4360   \global\advance\outerrecurse \minusone}
4361
4362\protected\def\doloop#1%
4363  {\global\advance\outerrecurse \plusone
4364   \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#1}%
4365   \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel
4366   \let\endofloop\syst_helpers_loop
4367   \syst_helpers_loop1} % no \plusone else \recurselevel wrong
4368
4369% for instance:
4370%
4371% \protected\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4
4372%   {\global\advance\outerrecurse \plusone
4373%    \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#4}%
4374%    \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel
4375%    \ifnum#3>\zerocount\relax
4376%      \ifnum#2<#1\relax
4377%        \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit
4378%      \else
4379%        \let\syst_helpers_stepwise_next\syst_helpers_stepwise_recurse
4380%      \fi
4381%    \else
4382%      \ifnum#3<\zerocount\relax
4383%        \ifnum#1<#2\relax
4384%          \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit
4385%        \else
4386%          \let\syst_helpers_stepwise_next\syst_helpers_stepwise_reverse
4387%        \fi
4388%      \else
4389%        \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit
4390%      \fi
4391%    \fi\normalexpanded{\syst_helpers_stepwise_next{\number#1}{\number#2}{\number#3}}}
4392%
4393% faster:
4394%
4395% \protected\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4
4396%   {\global\advance\outerrecurse \plusone
4397%    \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#4}%
4398%    \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel
4399%    \csname @swr%
4400%      \ifnum#3>\zerocount
4401%        \ifnum#2<#1\else d\fi
4402%      \else\ifnum#3<\zerocount
4403%        \ifnum#1<#2\else r\fi
4404%      \fi\fi
4405%    \expandafter\endcsname\normalexpanded{{\number#1}{\number#2}{\number#3}}}
4406%
4407% \let\@swr \syst_helpers_stepwise_exit
4408% \let\@swrd\syst_helpers_stepwise_recurse
4409% \let\@swrr\syst_helpers_stepwise_reverse
4410%
4411% nicer:
4412
4413\installsystemnamespace{recursestepwise}
4414
4415\protected\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4
4416  {\global\advance\outerrecurse \plusone
4417   \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#4}%
4418   \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel
4419   \csname\??recursestepwise
4420     % we need the x in order to avoid the \relax that tex adds
4421     \ifnum#3>\zerocount
4422       \ifnum#2<#1x\else d\fi
4423     \else\ifnum#3<\zerocount
4424       \ifnum#1<#2x\else r\fi
4425     \else
4426       x%
4427     \fi\fi
4428   \expandafter\endcsname\normalexpanded{{\number#1}{\number#2}{\number#3}}}
4429 % \expandafter\endcsname\expandafter{\number#1\expandafter}\expandafter{\number#2\expandafter}\expandafter{\number#3}}
4430
4431\letvalue{\??recursestepwise x}\syst_helpers_stepwise_exit
4432\letvalue{\??recursestepwise d}\syst_helpers_stepwise_recurse
4433\letvalue{\??recursestepwise r}\syst_helpers_stepwise_reverse
4434
4435% quite okay too, but untested
4436%
4437% \def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4
4438%   {\global\advance\outerrecurse \plusone
4439%    \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#4}%
4440%    \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel
4441%    \normalexpanded
4442%      {\ifnum#3>\zerocount
4443%         \ifnum#2<#1
4444%           \syst_helpers_stepwise_exit
4445%         \else
4446%           \syst_helpers_stepwise_recurse
4447%         \fi
4448%       \else
4449%         \ifnum#3<\zerocount
4450%           \ifnum#1<#2
4451%             \syst_helpers_stepwise_exit
4452%           \else
4453%             \syst_helpers_stepwise_reverse
4454%           \fi
4455%         \else
4456%           \syst_helpers_stepwise_exit
4457%         \fi
4458%       \fi{\number#1}{\number#2}{\number#3}}}
4459
4460%D For special purposes:
4461
4462\newcount\fastloopindex
4463\newcount\fastloopfinal
4464
4465\let\m_syst_helpers_fast_loop_cs\relax
4466
4467\protected\def\dofastloopcs#1%
4468  {\fastloopfinal#1\relax
4469   \ifcase\fastloopfinal
4470     \expandafter\gobbleoneargument
4471   \else
4472     \expandafter\syst_helpers_fast_loop_cs
4473   \fi}
4474
4475\protected\def\syst_helpers_fast_loop_cs#1%
4476  {\let\m_syst_helpers_fast_loop_cs#1%
4477   \fastloopindex\plusone
4478   \syst_helpers_fast_loop_cs_step}
4479
4480\protected\def\syst_helpers_fast_loop_cs_step
4481  {\ifnum\fastloopindex>\fastloopfinal
4482     \let\m_syst_helpers_fast_loop_cs\relax
4483   \else
4484     \m_syst_helpers_fast_loop_cs
4485     \advance\fastloopindex\plusone
4486     \expandafter\syst_helpers_fast_loop_cs_step
4487   \fi}
4488
4489% Helper:
4490
4491\protected\def\resetrecurselevel{\let\recurselevel\!!zerocount}
4492
4493\let\recurselevel \!!zerocount
4494\let\recurseaction\relax
4495\let\recursestring\empty
4496
4497% \appendtoks \resetrecurselevel \to \everydump
4498
4499%D \macros
4500%D   {doloopoverlist}
4501%D
4502%D \starttyping
4503%D \doloopoverlist {red,green,blue} {
4504%D      \setuppalet[\recursestring]
4505%D      \doloopoverlist {light,normal,dark} {
4506%D          \blackrule[color=\recursestring,width=20cm,height=2cm,depth=0cm]\par
4507%D      }
4508%D  }
4509%D \stoptyping
4510%D
4511%D or:
4512%D
4513%D \starttyping
4514%D \doloopoverlist {red,green,blue} {
4515%D      \setuppalet[#1]
4516%D      \doloopoverlist {light,normal,dark} {
4517%D          \blackrule[color=##1,width=20cm,height=2cm,depth=0cm]\par
4518%D      }
4519%D  }
4520%D \stoptyping
4521
4522\protected\def\doloopoverlist#1#2%
4523  {\global\advance\outerrecurse\plusone
4524   \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1{\edef\recursestring{##1}#2}%
4525   \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recursestring
4526   \normalexpanded{\processcommalist[#1]{\expandafter\noexpand\csname\??recurseaction\the\outerrecurse\endcsname}}%
4527   \expandafter\let\expandafter\recursestring\csname\??recurseindex\the\outerrecurse\endcsname
4528   \global\advance\outerrecurse\minusone}
4529
4530%D \macros
4531%D   {newevery,everyline,EveryLine,EveryPar}
4532%D
4533%D Lets skip to something quite different. It's common use to use \type {\everypar}
4534%D for special purposes. In \CONTEXT\ we use this primitive for locating sidefloats.
4535%D This means that when user assignments to \type {\everypar} can interfere with
4536%D those of the package. We therefore introduce \type {\EveryPar}.
4537%D
4538%D The same goes for \type {\EveryLine}. Because \TEX\ offers no \type {\everyline}
4539%D primitive, we have to call for \type {\everyline} when we are working on a line
4540%D by line basis. Just by calling \type {\EveryPar{}} and \type {\EveryLine{}} we
4541%D restore the old situation.
4542
4543% \dorecurse{2}{
4544%     \expanded{\everypar{before \recurselevel\space}}
4545%     \EveryPar{x } [before \recurselevel\space x] \par
4546%     \EveryPar{y } [before \recurselevel\space y] \par
4547%     \EveryPar{}   [before \recurselevel]   \par
4548%     \EveryPar{x } \EveryPar{y } \EveryPar{} [before \recurselevel] \par
4549%     \EveryPar{y } \everypar{before } [before] \par
4550% }
4551
4552\installsystemnamespace{extraevery}
4553
4554% \protected\def\newevery#1#2%
4555%   {\ifx#1\everypar\else\newtoks#1\fi% we test for redefinition elsewhere
4556%    \ifx#2\relax\else\ifdefined#2\else
4557%      \expandafter\newtoks\csname\??extraevery\csstring#1\endcsname
4558%      \def#2{\syst_helpers_every#1}%
4559%    \fi\fi}
4560%
4561% \protected\def\syst_helpers_every#1%
4562%   {\expandafter\removetoks\expandafter\the\csname\??extraevery\csstring#1\endcsname\from#1%
4563%    \expandafter\appendtoks\expandafter\the\csname\??extraevery\csstring#1\endcsname\to  #1%
4564%    \csname\??extraevery\csstring#1\endcsname}
4565
4566\protected\def\newevery#1#2%
4567  {\ifx#1\everypar\else\newtoks#1\fi% we test for redefinition elsewhere
4568   \ifx#2\relax\else\ifdefined#2\else
4569     \expandafter\newtoks\csname\??extraevery\csstring#1\endcsname
4570     \edef#2{\syst_helpers_every#1\csname\??extraevery\csstring#1\endcsname}%
4571   \fi\fi}
4572
4573\protected\def\syst_helpers_every#1#2%
4574  {\removetoks\the#2\from#1%
4575   \appendtoks\the#2\to  #1%
4576   #2}
4577
4578%D This one permits definitions like:
4579
4580\newevery \everypar  \EveryPar  % we get a warning which is ok
4581\newevery \everyline \EveryLine
4582
4583%D and how about:
4584
4585% \newtoks \neverypar
4586% \newtoks \neveryendpar
4587%
4588% \protected\def\syst_helpers_forgotten_endpar
4589%   {\the\neveryendpar\normalpar}
4590%
4591% \protected\def\forgeteverypar
4592%   {\everypar{\the\neverypar}%
4593%    \let\endpar\syst_helpers_forgotten_endpar}
4594%
4595% \protected\def\finishpar
4596%   {\ifvmode\else\par\fi}
4597
4598\newtoks \neverypar
4599
4600\protected\def\forgeteverypar
4601  {\everypar{\the\neverypar}}
4602
4603%D Which we're going to use indeed! When the second argument equals \type {\relax},
4604%D the first token list is created unless it is already defined.
4605%D
4606%D Technically spoken we could have used the method we are going to present in the
4607%D visual debugger. First we save the primitive \type{\everypar}:
4608%D
4609%D \starttyping
4610%D \let\normaleverypar=\everypar
4611%D \stoptyping
4612%D
4613%D Next we allocate a \TOKENLIST\ named \type{\everypar}, which means that
4614%D \type{\everypar} is no longer a primitive but something like \type{\toks44}.
4615%D
4616%D \starttyping
4617%D \newtoks\everypar
4618%D \stoptyping
4619%D
4620%D Because \TEX\ now executes \type{\normaleverypar} instead of \type{\everypar}, we
4621%D are ready to assign some tokens to this internally known and used \TOKENLIST.
4622%D
4623%D \starttyping
4624%D \normaleverypar={all the things the system wants to do \the\everypar}
4625%D \stoptyping
4626%D
4627%D Where the user can provide his own tokens to be expanded every time he expects
4628%D them to expand.
4629%D
4630%D \starttyping
4631%D \everypar={something the user wants to do}
4632%D \stoptyping
4633%D
4634%D We don't use this method because it undoubtly leads to confusing situations,
4635%D especially when other packages are used, but it's this kind of tricks that make
4636%D \TEX\ so powerful.
4637
4638%D \macros
4639%D   {convertargument,convertcommand,convertvalue}
4640%D
4641%D Some persistent experimenting led us to the next macro. This macro converts a
4642%D parameter or an expanded macro to it's textual meaning.
4643%D
4644%D \starttyping
4645%D \convertargument ... \to \command
4646%D \stoptyping
4647%D
4648%D For example,
4649%D
4650%D \starttyping
4651%D \convertargument{one \two \three{four}}\to\ascii
4652%D \stoptyping
4653%D
4654%D The resulting macro \type{\ascii} can be written to a file or the terminal
4655%D without problems. In \CONTEXT\ we use this macro for generating registers and
4656%D tables of contents.
4657%D
4658%D The second conversion alternative accepts a command:
4659%D
4660%D \starttyping
4661%D \convertcommand\command\to\ascii
4662%D \stoptyping
4663%D
4664%D Both commands accept the prefix \type{\doglobal} for global assignments.
4665
4666\protected\def\convertvalue#1\to
4667  {\expandafter\convertcommand\csname#1\endcsname\to}
4668
4669\protected\def\defconvertedvalue#1#2% less sensitive for \to
4670  {\expandafter\defconvertedcommand\expandafter#1\csname#2\endcsname}
4671
4672%D \macros
4673%D   {doifassignmentelse}
4674%D
4675%D A lot of \CONTEXT\ commands take optional arguments, for instance:
4676%D
4677%D \starttyping
4678%D \dothisorthat[alfa,beta]
4679%D \dothisorthat[first=foo,second=bar]
4680%D \dothisorthat[alfa,beta][first=foo,second=bar]
4681%D \stoptyping
4682%D
4683%D Although a combined solution is possible, we prefer a seperation. The next
4684%D command takes care of propper handling of such multi||faced commands.
4685%D
4686%D \starttyping
4687%D \doifassignmentelse {...} {then ...} {else ...}
4688%D \stoptyping
4689
4690\def\syst_helpers_check_if_assignment_else#1=#2#3^^^^0004{\if#2^^^^0003}%
4691\def\syst_helpers_check_else_assignment_if#1=#2#3^^^^0004{\unless\if#2^^^^0003}%
4692
4693\protected\def\doifelseassignment#1%
4694  {\expandafter\syst_helpers_check_if_assignment_else\detokenize{#1}=^^^^0003^^^^0003^^^^0004%
4695     \expandafter\secondoftwoarguments
4696   \else
4697     \expandafter\firstoftwoarguments
4698   \fi}
4699
4700\protected\def\doifelseassignmentcs#1#2#3%
4701  {\expandafter\syst_helpers_check_if_assignment_else\detokenize{#1}=^^^^0003^^^^0003^^^^0004%
4702     \expandafter#3%
4703   \else
4704     \expandafter#2%
4705   \fi}
4706
4707\let\doifassignmentelse  \doifelseassignment
4708\let\doifassignmentelsecs\doifelseassignmentcs
4709
4710\newif\ifassignment
4711
4712\protected\def\docheckassignment#1%
4713  {\expandafter\syst_helpers_check_if_assignment_else\detokenize{#1}=^^^^0003^^^^0003^^^^0004%
4714     \assignmentfalse
4715   \else
4716     \assignmenttrue
4717   \fi}
4718
4719%D These can be used for cases where we want less tracing noise.
4720
4721\protected\def\validassignment#1%
4722  {\expandafter\syst_helpers_check_else_assignment_if\detokenize{#1}=^^^^0003^^^^0003^^^^0004}
4723
4724\protected\def\novalidassignment#1%
4725  {\expandafter\syst_helpers_check_if_assignment_else\detokenize{#1}=^^^^0003^^^^0003^^^^0004}
4726
4727%D In \ETEX\ we can use \type {\detokenize} and gain some speed, but in general far
4728%D less that 1\% for \type {\convertargument} and nil for \type {\convertcommand}.
4729%D This macro is more robust than the pure \TEX\ one, something I found out when
4730%D primitives like \type {\jobname} were fed (or something undefined).
4731
4732\protected\def\convertargument#1\to#2{\dodoglobal\edef#2{\detokenize{#1}}}
4733\protected\def\convertcommand #1\to#2{\dodoglobal\edef#2{\expandafter\detokenize\expandafter{#1}}} % hm, only second is also ok
4734
4735\protected\def\defconvertedargument #1#2{\edef#1{\detokenize{#2}}}
4736\protected\def\defconvertedcommand  #1#2{\edef#1{\detokenize\expandafter{#2}}}
4737\protected\def\edefconvertedargument#1#2{\edef#1{#2}%
4738                                          \edef#1{\detokenize\expandafter{#1}}}
4739\protected\def\gdefconvertedargument#1#2{\xdef#1{\detokenize{#2}}}
4740\protected\def\gdefconvertedcommand #1#2{\xdef#1{\detokenize\expandafter{#2}}}
4741\protected\def\xdefconvertedargument#1#2{\xdef#1{#2}%
4742                                          \xdef#1{\detokenize\expandafter{#1}}}
4743
4744%D When you try to convert a primitive command, you'll find out that the \ETEX\
4745%D method fails on for instance \type {\jobname} in the sense that it returns the
4746%D filename instead of just \type {\jobname}. So far this does not give real
4747%D problems.
4748
4749%D This is typically a macro that one comes to after reading the \TEX book
4750%D carefully. Even then, the definite solution was found after rereading the \TEX
4751%D book. The first implementation was:
4752%D
4753%D \starttyping
4754%D \def\doconvertargument#1->#2\\\\{#2}
4755%D \stoptyping
4756%D
4757%D The \type {-}, the delimiter \type {\\\\} and the the second argument are
4758%D completely redundant.
4759
4760%D \macros
4761%D   {showvalue}
4762%D
4763%D Ahandy macro, for testing purposes only:
4764
4765\protected\def\showvalue#1%
4766  {\ifcsname#1\endcsname
4767     \expandafter\show\csname#1\endcsname
4768   \else
4769     \show\undefined
4770   \fi}
4771
4772%D \macros
4773%D   {doifmeaningelse}
4774%D
4775%D We can use both commands in testing, but alas, not all meanings expand to
4776%D something \type {->}. This is no problem in the \ETEX\ implementation, but since
4777%D we want compatibility, we need:
4778%D
4779%D \starttyping
4780%D \doifmeaningelse {\next} {\something} {true} {false}
4781%D \stoptyping
4782%D
4783%D Watch the one level expansion of the second argument.
4784
4785\protected\def\doifelsemeaning#1#2%
4786  {\edef\m_syst_string_one{\normalmeaning#1}%
4787   \def \m_syst_string_two{#2}%
4788   \edef\m_syst_string_two{\normalmeaning\m_syst_string_two}%
4789   \ifx\m_syst_string_one\m_syst_string_two
4790     \expandafter\firstoftwoarguments
4791   \else
4792     \expandafter\secondoftwoarguments
4793   \fi}
4794
4795\let\doifmeaningelse\doifelsemeaning
4796
4797%D \macros
4798%D   {doifsamestringselse,doifsamestring,doifnotsamestring}
4799%D
4800%D The next comparison macro converts the arguments into expanded strings. This
4801%D command can be used to compare for instance \type {\jobname} with a name stored
4802%D in a macro.
4803%D
4804%D \starttyping
4805%D \doifelse          {\jobname}{oeps}{YES}{NO}
4806%D \doifsamestringelse{\jobname}{oeps}{YES}{NO}
4807%D \stoptyping
4808
4809\def\syst_helpers_if_samestring_else#1#2#3#4%
4810  {\edef\m_syst_string_one{\detokenize\expandafter{\normalexpanded{#3}}}%
4811   \edef\m_syst_string_two{\detokenize\expandafter{\normalexpanded{#4}}}%
4812   \ifx\m_syst_string_one\m_syst_string_two\expandafter#1\else\expandafter#2\fi}
4813
4814\protected\def\doifelsesamestring{\syst_helpers_if_samestring_else\firstoftwoarguments\secondoftwoarguments}
4815\protected\def\doifsamestring    {\syst_helpers_if_samestring_else\firstofoneargument \gobbleoneargument   }
4816\protected\def\doifnotsamestring {\syst_helpers_if_samestring_else\gobbleoneargument  \firstofoneargument  }
4817
4818\let\doifsamestringelse\doifelsesamestring
4819
4820%D \macros
4821%D   {ConvertToConstant,ConvertConstantAfter}
4822%D
4823%D When comparing arguments with a constant, we can get into trouble when this
4824%D argument consists of tricky expandable commands. One solution for this is
4825%D converting the argument to a string of unexpandable characters. To make
4826%D comparison possible, we have to convert the constant too.
4827%D
4828%D \starttyping
4829%D \ConvertToConstant\doifelse {...} {...} {then ...} {else ...}
4830%D \stoptyping
4831%D
4832%D This construction is only needed when the first argument can give troubles.
4833%D Misuse can slow down processing.
4834%D
4835%D \starttyping
4836%D \ConvertToConstant\doifelse{\c!alfa}        {\c!alfa}{...}{...}
4837%D \ConvertToConstant\doifelse{alfa}           {\c!alfa}{...}{...}
4838%D \ConvertToConstant\doifelse{alfa}           {alfa}   {...}{...}
4839%D \ConvertToConstant\doifelse{alfa \alfa test}{\c!alfa}{...}{...}
4840%D \stoptyping
4841%D
4842%D In examples~2 and~3 both arguments equal, in~1 and~4 they differ.
4843
4844\protected\def\ConvertToConstant#1#2#3%
4845  {\edef\m_syst_string_one{\expandafter\detokenize\expandafter{#2}}%
4846   \edef\m_syst_string_two{\expandafter\detokenize\expandafter{#3}}%
4847   #1{\m_syst_string_one}{\m_syst_string_two}}
4848
4849%D When the argument \type{#1} consists of commands, we had better use
4850%D
4851%D \starttyping
4852%D \ConvertConstantAfter\processaction[#1][...]
4853%D \ConvertConstantAfter\doifelse{#1}{\v!something}{}{}
4854%D \stoptyping
4855%D
4856%D This commands accepts things like:
4857%D
4858%D \starttyping
4859%D \v!constant
4860%D constant
4861%D \hbox to \hsize{\rubish}
4862%D \stoptyping
4863%D
4864%D As we will see in the core modules, this macro permits constructions like:
4865%D
4866%D \starttyping
4867%D \setupfootertexts[...][...]
4868%D \setupfootertexts[margin][...][...]
4869%D \setupfootertexts[\v!margin][...][...]
4870%D \stoptyping
4871%D
4872%D where \type {...} can be anything legally \TEX.
4873
4874\protected\def\CheckConstantAfter#1#2%
4875  {\expandafter\convertargument\v!prefix!\to\ascii
4876   \convertargument#1\to#2\relax
4877   \doifelseinstring\ascii{#2}
4878     {\expandafter\convertargument#1\to#2}
4879     {}}
4880
4881\protected\def\ConvertConstantAfter#1#2#3%
4882  {\CheckConstantAfter{#2}\asciia
4883   \CheckConstantAfter{#3}\asciib
4884   #1{\asciia}{\asciib}}
4885
4886%D \macros
4887%D   {assignifempty}
4888%D
4889%D We can assign a default value to an empty macro using:
4890%D
4891%D \starttyping
4892%D \assignifempty \macros {default value}
4893%D \stoptyping
4894%D
4895%D We don't explicitly test if the macro is defined.
4896
4897\protected\def\assignifempty#1#2% can be sped up
4898  {\doifnothing{#1}{\def#1{#2}}}
4899
4900%D \macros
4901%D   {gobbleuntil,grabuntil,gobbleuntilrelax,
4902%D    processbetween,processuntil}
4903%D
4904%D In \TEX\ gobbling usually stand for skipping arguments, so here are our gobbling
4905%D macros.
4906%D
4907%D In \CONTEXT\ we use a lot of \type {\start}||\type {\stop} like constructions.
4908%D Sometimes, the \type {\stop} is used as a hard coded delimiter like in: %D
4909%D \starttyping
4910%D \protected\def\startcommand#1\stopcommand%
4911%D   {... #1 ...}
4912%D \stoptyping
4913%D
4914%D In many cases the \type {\start}||\type {\stop} pair is defined at format
4915%D generation time or during a job. This means that we cannot hardcode the \type
4916%D {\stop} criterium. Only after completely understanding \type {\csname} and \type
4917%D {\expandafter} I was able to to implement a solution, starting with:
4918%D
4919%D \starttyping
4920%D \grabuntil{stop}\command
4921%D \stoptyping
4922%D
4923%D This commands executes, after having encountered \type {\stop} the command \type
4924%D {\command}. This command receives as argument the text preceding the \type
4925%D {\stop}. This means that:
4926%D
4927%D \starttyping
4928%D \protected\def\starthello%
4929%D   {\grabuntil{stophello}\message}
4930%D
4931%D \starthello Hello world!\stophello
4932%D \stoptyping
4933%D
4934%D results in: \type{\message{Hello world!}}.
4935
4936\let\syst_helpers_grab_indeed\relax
4937
4938\protected\def\syst_helpers_grab#1#2%
4939  {\def\syst_helpers_grab_indeed##1#1{#2{##1}}\syst_helpers_grab_indeed}
4940
4941\protected\def\grabuntil#1%
4942  {\expandafter\syst_helpers_grab\expandafter{\csname#1\endcsname}}
4943
4944%D The next command build on this mechanism:
4945%D
4946%D \starttyping
4947%D \processbetween{string}\command
4948%D \stoptyping
4949%D
4950%D Here:
4951%D
4952%D \starttyping
4953%D \processbetween{hello}\message
4954%D \starthello Hello again!\stophello
4955%D \stoptyping
4956%D
4957%D leads to: \type{\message{Hello again!}}. The command
4958%D
4959%D \starttyping
4960%D \gobbleuntil{sequence}
4961%D \stoptyping
4962%D
4963%D is related to these commands. This one simply throws away
4964%D everything preceding \type{\command}.
4965
4966\let\syst_helpers_gobble_indeed\relax
4967
4968\protected\def\processbetween#1#2%
4969  {\setvalue{\s!start#1}{\grabuntil{\s!stop#1}{#2}}}
4970
4971\protected\def\gobbleuntil#1%
4972  {\def\syst_helpers_gobble_indeed##1#1{}\syst_helpers_gobble_indeed}
4973
4974\protected\def\gobbleuntilrelax#1\relax
4975  {}
4976
4977% experimental
4978
4979\protected\def\gobblenested#1#2#3%
4980  {\normalexpanded{\def\noexpand\next##1\csname#2\endcsname}{\csname#3\endcsname}%
4981   \next}%
4982
4983%D The next one simply expands the pickup up tokens.
4984%D
4985%D \starttyping
4986%D \processuntil{sequence}
4987%D \stoptyping
4988
4989\let\syst_helpers_until_indeed\relax
4990
4991\protected\def\processuntil#1%
4992  {\def\syst_helpers_until_indeed##1#1{##1}\syst_helpers_until_indeed}
4993
4994%D \macros
4995%D   {groupedcommand}
4996%D
4997%D Commands often manipulate argument as in:
4998%D
4999%D \starttyping
5000%D \def\doezomaarwat#1{....#1....}
5001%D \stoptyping
5002%D
5003%D A disadvantage of this approach is that the tokens that form \type{#1} are fixed
5004%D the the moment the argument is read in. Normally this is no problem, but for
5005%D instance verbatim environments adapt the \CATCODES\ of characters and therefore
5006%D are not always happy with already fixed tokens.
5007%D
5008%D Another problem arises when the argument is grouped not by \type {{}} but by
5009%D \type {\bgroup} and \type {\egroup}. Such an argument fails, because the \type
5010%D {\bgroup} is een as the argument (which is quite normal).
5011%D
5012%D The next macro offers a solution for both unwanted
5013%D situations:
5014%D
5015%D \starttyping
5016%D \groupedcommand {before} {after}
5017%D \stoptyping
5018%D
5019%D Which can be used like:
5020%D
5021%D \starttyping
5022%D \def\cite%
5023%D   {\groupedcommand{\rightquote\rightquote}{\leftquote\leftquote}}
5024%D \stoptyping
5025%D
5026%D This command is equivalent to, but more 'robust' than:
5027%D
5028%D \starttyping
5029%D \def\cite#1%
5030%D   {\rightquote\rightquote#1\leftquote\leftquote}
5031%D \stoptyping
5032%D
5033%D \starttyping
5034%D \def\rightword%
5035%D   {\groupedcommand{\hfill\hbox}{\parfillskip\zeropoint}}
5036%D
5037%D .......... \rightword{the right way}
5038%D \stoptyping
5039%D
5040%D Here \TEX\ typesets \type {\bf the right way} unbreakable at the end of the line.
5041%D The solution mentioned before does not work here. We also handle
5042%D
5043%D \starttyping
5044%D to be \bold{bold} or not, that's the question
5045%D \stoptyping
5046%D
5047%D and
5048%D
5049%D \starttyping
5050%D to be {\bold bold} or not, that's the question
5051%D \stoptyping
5052%D
5053%D This alternative checks for a \type {\bgroup} token first. The internal
5054%D alternative does not accept the box handling mentioned before, but further
5055%D nesting works all right. The extra \type {\bgroup}||\type {\egroup} is needed to
5056%D keep \type {\m_syst_helpers_handle_group_after} both into sight and local.
5057
5058\let\m_syst_helpers_handle_group_after \relax
5059\let\m_syst_helpers_handle_group_before\relax
5060
5061% keep:
5062%
5063% \protected\def\syst_helpers_handle_group_normal#1#2%
5064%   {\bgroup
5065%    \def\m_syst_helpers_handle_group_before{\bgroup#1\bgroup\aftergroup\m_syst_helpers_handle_group_after}% can't we remove the second \bgroup
5066%    \def\m_syst_helpers_handle_group_after {#2\egroup\egroup}% and one \egroup here?
5067%    \afterassignment\m_syst_helpers_handle_group_before
5068%    \let\next=}
5069
5070% \protected\def\syst_helpers_handle_group_normal#1#2%
5071%   {\bgroup
5072%    \def\m_syst_helpers_handle_group_before{#1}%
5073%    \def\m_syst_helpers_handle_group_after {#2}%
5074%    \afterassignment\m_syst_helpers_handle_group_normal_before
5075%    \let\next=}
5076%
5077% \def\m_syst_helpers_handle_group_normal_before
5078%   {\bgroup
5079%    \m_syst_helpers_handle_group_before
5080%    \bgroup
5081%    \aftergroup\m_syst_helpers_handle_group_normal_after}
5082%
5083% \def\m_syst_helpers_handle_group_normal_after
5084%   {\m_syst_helpers_handle_group_after
5085%    \egroup
5086%    \egroup}
5087%
5088% \protected\def\syst_helpers_handle_group_simple#1#2% no inner group (so no kerning interference)
5089%   {\bgroup
5090%    \def\m_syst_helpers_handle_group_before{#1}%
5091%    \def\m_syst_helpers_handle_group_after {#2}%
5092%    \afterassignment\m_syst_helpers_handle_group_simple_before
5093%    \let\next=}
5094%
5095% \def\m_syst_helpers_handle_group_simple_before
5096%   {\bgroup
5097%    \aftergroup\m_syst_helpers_handle_group_simple_after
5098%    \m_syst_helpers_handle_group_before}
5099%
5100% \def\m_syst_helpers_handle_group_simple_after
5101%   {\m_syst_helpers_handle_group_after
5102%    \egroup}%
5103%
5104% \protected\def\syst_helpers_handle_group_pickup#1#2#3% no inner group (so no kerning interference)
5105%   {\bgroup
5106%    \def\m_syst_helpers_handle_group_before{#1}%
5107%    \def\m_syst_helpers_handle_group_after {#2\egroup#3}%
5108%    \afterassignment\m_syst_helpers_handle_group_pickup_before
5109%    \let\next=}
5110%
5111% \def\m_syst_helpers_handle_group_pickup_before
5112%   {\bgroup
5113%    \aftergroup\m_syst_helpers_handle_group_after
5114%    \m_syst_helpers_handle_group_before}
5115%
5116% \protected\def\syst_helpers_handle_group_nop
5117%   {\ifnum\currentgrouptype=\semisimplegroupcode
5118%      \expandafter\syst_helpers_handle_group_nop_a
5119%    \else
5120%      \expandafter\syst_helpers_handle_group_nop_b
5121%    \fi}
5122%
5123% \def\syst_helpers_handle_group_nop_a#1#2%
5124%   {\def\m_syst_helpers_handle_group_after{#2\endgroup}%
5125%    \begingroup
5126%    \aftergroup\m_syst_helpers_handle_group_after
5127%    #1}
5128%
5129% \def\syst_helpers_handle_group_nop_b#1#2%
5130%   {\def\m_syst_helpers_handle_group_after{#2\egroup}%
5131%    \bgroup
5132%    \aftergroup\m_syst_helpers_handle_group_after
5133%    #1}
5134
5135\protected\def\syst_helpers_handle_group_nop
5136  {\ifnum\currentgrouptype=\semisimplegroupcode
5137     \expandafter\syst_helpers_handle_group_nop_a
5138   \else
5139     \expandafter\syst_helpers_handle_group_nop_b
5140   \fi}
5141
5142\def\syst_helpers_handle_group_nop_a
5143  {\begingroup
5144   \aftergroup\m_syst_helpers_handle_group_a
5145   \aftergroup\endgroup
5146   \m_syst_helpers_handle_group_b}
5147
5148\def\syst_helpers_handle_group_nop_b
5149  {\bgroup
5150   \aftergroup\m_syst_helpers_handle_group_a
5151   \aftergroup\egroup
5152   \m_syst_helpers_handle_group_b}
5153
5154\protected\def\syst_helpers_handle_group_normal
5155  {\bgroup
5156   \afterassignment\m_syst_helpers_handle_group_normal_before
5157   \let\next=}
5158
5159\def\m_syst_helpers_handle_group_normal_before
5160  {\bgroup
5161   \m_syst_helpers_handle_group_b
5162   \bgroup
5163   \aftergroup\m_syst_helpers_handle_group_a
5164   \aftergroup\egroup
5165   \aftergroup\egroup}
5166
5167\protected\def\syst_helpers_handle_group_simple% no inner group (so no kerning interference)
5168  {\bgroup
5169   \afterassignment\m_syst_helpers_handle_group_simple_before
5170   \let\next=}
5171
5172\def\m_syst_helpers_handle_group_simple_before
5173  {\bgroup
5174   \aftergroup\m_syst_helpers_handle_group_simple_after
5175   \m_syst_helpers_handle_group_b}
5176
5177\def\m_syst_helpers_handle_group_simple_after
5178  {\m_syst_helpers_handle_group_a
5179   \egroup}%
5180
5181\protected\def\syst_helpers_handle_group_pickup% no inner group (so no kerning interference)
5182  {\bgroup
5183   \afterassignment\m_syst_helpers_handle_group_pickup_before
5184   \let\next=}
5185
5186\def\m_syst_helpers_handle_group_pickup_before
5187  {\bgroup
5188   \aftergroup\m_syst_helpers_handle_group_a
5189   \aftergroup\egroup
5190   \aftergroup\m_syst_helpers_handle_group_p
5191   \m_syst_helpers_handle_group_b}
5192
5193\protected\def\syst_helpers_handle_group_nop_x
5194  {\ifnum\currentgrouptype=\semisimplegroupcode
5195     \begingroup
5196     \aftergroup\endgroup
5197   \else
5198     \bgroup
5199     \aftergroup\egroup
5200   \fi
5201   \m_syst_helpers_handle_group_b}
5202
5203\protected\def\syst_helpers_handle_group_normal_x
5204  {\bgroup
5205   \afterassignment\m_syst_helpers_handle_group_normal_before_x
5206   \let\next=}
5207
5208\def\m_syst_helpers_handle_group_normal_before_x
5209  {\bgroup
5210   \m_syst_helpers_handle_group_b
5211   \bgroup
5212   \aftergroup\egroup
5213   \aftergroup\egroup}
5214
5215%D I considered it a nuisance that
5216%D
5217%D \starttyping
5218%D \color[green]
5219%D   {as grass}
5220%D \stoptyping
5221%D
5222%D was not interpreted as one would expect. This is due to the fact that \type
5223%D {\futurelet} obeys blank spaces, and a line||ending token is treated as a blank
5224%D space. So the final implementation became:
5225
5226% \protected\def\groupedcommand#1#2%
5227%   {\doifelsenextbgroup{\syst_helpers_handle_group_normal{#1}{#2}}{\syst_helpers_handle_group_nop{#1}{#2}}}
5228%
5229% \protected\def\groupedcommandcs#1#2%
5230%   {\doifelsenextbgroup{\syst_helpers_handle_group_normal{#1}{#2}}{\syst_helpers_handle_group_nop{#1}{#2}}}
5231%
5232% \protected\def\triggergroupedcommand#1%
5233%   {\doifelsenextbgroup{\syst_helpers_handle_group_normal{#1}{}}{\syst_helpers_handle_group_nop{#1}{}}}
5234%
5235% \protected\def\triggergroupedcommandcs#1%
5236%   {\doifelsenextbgroup{\syst_helpers_handle_group_normal{#1}{}}{\syst_helpers_handle_group_nop{#1}{}}}
5237%
5238% \protected\def\simplegroupedcommand#1#2%
5239%   {\doifelsenextbgroup{\syst_helpers_handle_group_simple{#1}{#2}}{\syst_helpers_handle_group_nop{#1}{#2}}}
5240%
5241% \protected\def\pickupgroupedcommand#1#2#3%
5242%   {\doifelsenextbgroup{\syst_helpers_handle_group_pickup{#1}{#2}{#3}}{\syst_helpers_handle_group_nop{#1}{#2}}}
5243
5244\protected\def\groupedcommand#1#2%
5245  {\def\m_syst_helpers_handle_group_b{#1}%
5246   \def\m_syst_helpers_handle_group_a{#2}%
5247   \doifelsenextbgroupcs\syst_helpers_handle_group_normal\syst_helpers_handle_group_nop}
5248
5249\protected\def\groupedcommandcs#1#2%
5250  {\let\m_syst_helpers_handle_group_b#1%
5251   \let\m_syst_helpers_handle_group_a#2%
5252   \doifelsenextbgroupcs\syst_helpers_handle_group_normal\syst_helpers_handle_group_nop}
5253
5254\protected\def\simplegroupedcommand#1#2%
5255  {\def\m_syst_helpers_handle_group_b{#1}%
5256   \def\m_syst_helpers_handle_group_a{#2}%
5257   \doifelsenextbgroupcs\syst_helpers_handle_group_simple\syst_helpers_handle_group_nop}
5258
5259\protected\def\pickupgroupedcommand#1#2#3%
5260  {\def\m_syst_helpers_handle_group_b{#1}%
5261   \def\m_syst_helpers_handle_group_a{#2}%
5262   \def\m_syst_helpers_handle_group_p{#3}%
5263   \doifelsenextbgroupcs\syst_helpers_handle_group_pickup\syst_helpers_handle_group_nop}
5264
5265\protected\def\triggergroupedcommand#1%
5266  {\def\m_syst_helpers_handle_group_b{#1}%
5267   \doifelsenextbgroupcs\syst_helpers_handle_group_normal_x\syst_helpers_handle_group_nop_x}
5268
5269\protected\def\triggergroupedcommandcs#1%
5270  {\let\m_syst_helpers_handle_group_b#1%
5271   \doifelsenextbgroupcs\syst_helpers_handle_group_normal_x\syst_helpers_handle_group_nop_x}
5272
5273%D Users should be aware of the fact that grouping can interfere with ones paragraph
5274%D settings that are executed after the paragraph is closed. One should therefore
5275%D explictly close the paragraph with \type {\par}, else the settings will be
5276%D forgotten and not applied. So it's:
5277%D
5278%D \starttyping
5279%D \def\BoldRaggedCenter%
5280%D   {\groupedcommand{\raggedcenter\bf}{\par}}
5281%D \stoptyping
5282
5283%D \macros
5284%D   {checkdefined}
5285%D
5286%D The bigger the system, the greater the change that user defined commands collide
5287%D with those that are part of the system. The next macro gives a warning when a
5288%D command is already defined. We considered blocking the definition, but this is
5289%D not always what we want.
5290%D
5291%D \starttyping
5292%D \checkdefined {category} {class} {command}
5293%D \stoptyping
5294%D
5295%D The user is warned with the suggestion to use \type {CAPITALS}. This suggestion
5296%D is feasible, because \CONTEXT only defines lowcased macros.
5297
5298\protected\def\showdefinederror#1#2%
5299  {\writestatus\m!system{#1 #2 replaces a macro, use CAPITALS!}}
5300
5301\protected\def\checkdefined#1#2#3%
5302  {\doifdefined{#3}{\showdefinederror{#2}{#3}}}
5303
5304%D \macros
5305%D   {GotoPar,GetPar}
5306%D
5307%D Typesetting a paragraph in a special way can be done by first grabbing the
5308%D contents of the paragraph and processing this contents grouped. The next macro
5309%D for instance typesets a paragraph in boldface.
5310%D
5311%D \starttyping
5312%D \def\remark#1\par%
5313%D   {\bgroup\bf#1\egroup}
5314%D \stoptyping
5315%D
5316%D This macro has to be called like
5317%D
5318%D \starttyping
5319%D \remark some text ... ending with \par
5320%D \stoptyping
5321%D
5322%D Instead of \type {\par} we can of course use an empty line. When we started
5323%D typesetting with \TEX, we already had produced lots of text in plain \ASCII. In
5324%D producing such simple formatted texts, we adopted an open layout, and when
5325%D switching to \TEX, we continued this open habit. Although \TEX\ permits a cramped
5326%D and badly formatted source, it adds to confusion and sometimes introduces errors.
5327%D So we prefer:
5328%D
5329%D \starttyping
5330%D \remark
5331%D
5332%D some text ... ending with an empty line
5333%D \stoptyping
5334%D
5335%D We are going to implement a mechanism that allows such open specifications. The
5336%D definition of the macro handling \type {\remark} becomes:
5337%D
5338%D \starttyping
5339%D \def\remark%
5340%D   {\BeforePar{\bgroup\bf}%
5341%D    \AfterPar{\egroup}%
5342%D    \GetPar}
5343%D \stoptyping
5344%D
5345%D A macro like \type {\GetPar} can be defined in several ways. The recent version,
5346%D the fourth one in a row, originally was far more complicated, but some
5347%D functionality has been moved to other macros.
5348%D
5349%D We start with the more simple but in some cases more appropriate alternative is
5350%D \type {\GotoPar}. This one leaves \type {\par} unchanged and is therefore more
5351%D robust. On the other hand, \type {\AfterPar} is not supported.
5352
5353\newtoks\BeforePar
5354\newtoks\AfterPar
5355
5356\protected\def\redowithpar\par
5357  {\doifelsenextchar\par\redowithpar\dodowithpar}%
5358
5359\protected\def\dowithpar#1#2%
5360  {\def\dodowithpar##1\par{#1##1#2}%
5361   \redowithpar\par}
5362
5363\protected\def\redogotopar\par
5364  {\doifelsenextchar\par\redogotopar\dodogotopar}%
5365
5366\protected\def\dogotopar#1%
5367  {\def\dodogotopar{#1}%
5368   \redogotopar\par}
5369
5370\protected\def\dogotoparcs#1%
5371  {\let\dodogotopar#1%
5372   \redogotopar\par}
5373
5374\ifdefined \ignorepars \else
5375    \protected\def\ignorepars{\dogotoparcs\relax}
5376\fi
5377
5378\protected\def\GetPar
5379  {\expanded
5380     {\dowithpar
5381        {\the\BeforePar
5382         \BeforePar\emptytoks}
5383        {\the\AfterPar
5384         \BeforePar\emptytoks
5385         \AfterPar\emptytoks}}}
5386
5387\protected\def\GotoPar
5388  {\expanded
5389     {\dogotopar
5390        {\the\BeforePar
5391         \BeforePar\emptytoks}}}
5392
5393%D \macros
5394%D   {dowithpargument,dowithwargument}
5395%D
5396%D The next macros are a variation on \type {\GetPar}. When macros expect an
5397%D argument, it interprets a grouped sequence of characters a one token. While this
5398%D adds to robustness and less ambiguous situations, we sometimes want to be a bit
5399%D more flexible, or at least want to be a bit more tolerant to user input.
5400%D
5401%D We start with a commands that acts on paragraphs. This
5402%D command is called as:
5403%D
5404%D \starttyping
5405%D \dowithpargument\command
5406%D \dowithpargument{\command ... }
5407%D \stoptyping
5408%D
5409%D In \CONTEXT\ we use this one to read in the titles of chapters, sections etc. The
5410%D commands responsible for these activities accept several alternative ways of
5411%D argument passing. In these examples, the \type {\par} can be omitted when an
5412%D empty line is present.
5413%D
5414%D \starttyping
5415%D \command{...}
5416%D \command ... \par
5417%D \command
5418%D   {...}
5419%D \command
5420%D   ... \par
5421%D \stoptyping
5422
5423\let\syst_helpers_next_par\relax
5424\let\syst_helpers_next_arg\relax
5425
5426\protected\def\dowithpargument#1%
5427  {\def\syst_helpers_next_par##1 \par{#1{##1}}%
5428   \def\syst_helpers_next_arg##1{#1{##1}}%
5429   \doifelsenextbgroup\syst_helpers_next_arg{\doifelsenextchar\par{#1{}}\syst_helpers_next_par}}
5430
5431%D The \type {p} in the previous command stands for paragraph. When we want to act
5432%D upon words we can use the \type{w} alternative.
5433%D
5434%D \starttyping
5435%D \dowithwargument\command
5436%D \dowithwargument{... \command ...}
5437%D \stoptyping
5438%D
5439%D The main difference bwteen two alternatives is in the handling of \type {\par}'s.
5440%D This time the space token acts as a delimiter.
5441%D
5442%D \starttyping
5443%D \command{...}
5444%D \command ...
5445%D \command
5446%D   {...}
5447%D \command
5448%D   ...
5449%D \stoptyping
5450
5451\let\syst_helpers_next_war\relax
5452\let\syst_helpers_next_arg\relax
5453
5454\protected\def\dowithwargument#1%
5455  {\def\syst_helpers_next_war##1 {#1{##1}}%
5456   \def\syst_helpers_next_arg##1{#1{##1}}%
5457   \doifelsenextbgroup\syst_helpers_next_arg\syst_helpers_next_war}
5458
5459%D \macros
5460%D   {dorepeat,dorepeatwithcommand}
5461%D
5462%D When doing repetitive tasks, we stromgly advice to use \type {\dorecurse}. The
5463%D next alternative however, suits better some of the \CONTEXT\ interface commands.
5464%D
5465%D \starttyping
5466%D \dorepeat[n*\command]
5467%D \stoptyping
5468%D
5469%D The value of the used \COUNTER\ can be called within
5470%D \type{\command} by \type{\repeater}.
5471%D
5472%D A slightly different alternative is:
5473%D
5474%D \starttyping
5475%D \dorepeatwithcommand[n*{...}]\command
5476%D \stoptyping
5477%D
5478%D When we call for something like:
5479%D
5480%D \starttyping
5481%D \dorepeatwithcommand[3*{Hello}]\message
5482%D \stoptyping
5483%D
5484%D we get ourselves three \type {\message{Hello}} messages in a row. In both
5485%D commands, the \type {n*} is optional. When this specification is missing, the
5486%D command executes once.
5487
5488\protected\def\dorepeatwithcommand[#1]%
5489  {\syst_helpers_repeat_with_command#1*\empty*\relax}
5490
5491\def\syst_helpers_repeat_with_command#1*#2#3*#4\relax#5%
5492  {\ifx#2\empty\syst_helpers_repeat_with_command_again[#1]#5\else\syst_helpers_repeat_with_command_indeed{#1}{#2}{#3}#5\fi}
5493
5494\def\syst_helpers_repeat_with_command_indeed#1#2#3#4%
5495  {\ifx#2\empty % redundant but gives cleaner extensions
5496     #4{#1}%
5497   \else\ifnum#1<\zerocount
5498    %\normalexpanded{\dorecurse{\number-\number#1}}{#4{-#2#3}}%
5499     \dorecurse{-#1}{#4{-#2#3}}%
5500   \else\ifx#2+%
5501     \dorecurse{#1}{#4{#3}}%
5502   \else
5503     \dorecurse{#1}{#4{#2#3}}%
5504   \fi\fi\fi}
5505
5506\def\syst_helpers_repeat_with_command_again[#1]#2%
5507  {#2{#1}}
5508
5509%D The extension hook permits something like:
5510%D
5511%D \starttyping
5512%D \bgroup
5513%D
5514%D \catcode`\*=\superscriptcatcode
5515%D
5516%D \gdef\syst_helpers_repeat_with_command_again[#1]%
5517%D   {\redodorepeatwithcommand#1*\empty*\relax}
5518%D
5519%D \gdef\redodorepeatwithcommand#1*#2#3*#4\relax#5%
5520%D   {\syst_helpers_repeat_with_command_indeed{#1}{#2}{#3}#5}
5521%D
5522%D \egroup
5523%D \stoptyping
5524%D
5525%D although one may wonder if changing the catcode of \type {*} is wise.
5526
5527%D \macros
5528%D   {doifstringinstringelse}
5529%D
5530%D The next macro is meant for situations where both strings are macros. This save
5531%D some unneeded expansion.
5532%D
5533%D \starttyping
5534%D \def\doifstringinstringelse#1#2%
5535%D   {\syst_helpers_do_if_in_string_else#1#2%
5536%D      \expandafter\firstoftwoarguments
5537%D    \else
5538%D      \expandafter\secondoftwoarguments
5539%D    \fi}
5540%D \stoptyping
5541%D
5542%D A bit faster is:
5543
5544\def\syst_helpers_if_instring_else_indeed#1%
5545  {\if#1@%
5546     \expandafter\secondoftwoarguments
5547   \else
5548     \expandafter\firstoftwoarguments
5549   \fi}
5550
5551\def\doifelsestringinstring#1#2%
5552  {\expandafter\def\expandafter\syst_helpers_if_instring_else\expandafter##\expandafter1#1##2##3^^^^0004%
5553     {\syst_helpers_if_instring_else_indeed##2}%
5554   \expandafter\expandafter\expandafter\syst_helpers_if_instring_else\expandafter#2#1@@^^^^0004}
5555
5556\let\doifstringinstringelse\doifelsestringinstring
5557
5558%D \macros
5559%D   {appendtoks,prependtoks,appendtoksonce,prependtoksonce,
5560%D    doifintokselse,flushtoks,dotoks}
5561%D
5562%D We use tokenlists sparsely within \CONTEXT, because the comma separated lists are
5563%D more suitable for the user interface. Nevertheless we have:
5564%D
5565%D \starttyping
5566%D (\doglobal) \appendtoks ... \to\tokenlist
5567%D (\doglobal) \prependtoks ... \to\tokenlist
5568%D (\doglobal) \flushtoks\tokenlist
5569%D             \dotoks\tokenlist
5570%D \stoptyping
5571%D
5572%D These macros are clones of the ones implemented in page~378 of Knuth's \TEX book.
5573
5574\newtoks\t_syst_helpers_scratch
5575\let    \m_syst_helpers_scratch\empty
5576
5577% no longer \def but \let to target toks .. the space gobbling \relax will go
5578
5579% \protected\def\appendtoks     {\syst_helpers_append_toks      \relax}
5580% \protected\def\prependtoks    {\syst_helpers_prepend_toks     \relax}
5581% \protected\def\appendtoksonce {\syst_helpers_append_toks_once \relax}
5582% \protected\def\prependtoksonce{\syst_helpers_prepend_toks_once\relax}
5583%
5584% \def\syst_helpers_append_toks_indeed
5585%   {\dodoglobal\m_syst_helpers_scratch\doubleexpandafter{\expandafter\the\expandafter\m_syst_helpers_scratch\the\t_syst_helpers_scratch}}
5586%
5587% \def\syst_helpers_prepend_toks_indeed
5588%   {\dodoglobal\m_syst_helpers_scratch\doubleexpandafter{\expandafter\the\expandafter\t_syst_helpers_scratch\the\m_syst_helpers_scratch}}
5589%
5590% \def\syst_helpers_append_toks#1\to#2%
5591%   {\let\m_syst_helpers_scratch#2%
5592%    \t_syst_helpers_scratch\expandafter{\gobbleoneargument#1}%
5593%    \syst_helpers_append_toks_indeed}
5594%
5595% \def\syst_helpers_prepend_toks#1\to#2%
5596%   {\let\m_syst_helpers_scratch#2%
5597%    \t_syst_helpers_scratch\expandafter{\gobbleoneargument#1}%
5598%    \syst_helpers_prepend_toks_indeed}
5599%
5600% \def\syst_helpers_append_toks_once#1\to#2%
5601%   {\let\m_syst_helpers_scratch#2%
5602%    \t_syst_helpers_scratch\expandafter{\gobbleoneargument#1}%
5603%    \doifelseintoks\t_syst_helpers_scratch\m_syst_helpers_scratch
5604%      \donothing
5605%      \syst_helpers_append_toks_indeed}
5606%
5607% \def\syst_helpers_prepend_toks_once#1\to#2%
5608%   {\let\m_syst_helpers_scratch#2%
5609%    \t_syst_helpers_scratch\expandafter{\gobbleoneargument#1}%
5610%    \doifelseintoks\t_syst_helpers_scratch\m_syst_helpers_scratch
5611%      \donothing
5612%      \syst_helpers_prepend_toks_indeed}
5613
5614% \protected\def\appendtoks#1\to#2%
5615%   {\toksapp#2{#1}%
5616%    \ifx\dodoglobal\relax\else
5617%      \dodoglobal#2#2%
5618%    \fi}
5619%
5620% \protected\def\prependtoks#1\to#2%
5621%   {\tokspre#2{#1}%
5622%    \ifx\dodoglobal\relax\else
5623%       \dodoglobal#2#2%
5624%    \fi}
5625%
5626% \def\syst_helpers_append_toks_indeed
5627%   {\toksapp\m_syst_helpers_scratch\t_syst_helpers_scratch
5628%    \ifx\dodoglobal\relax\else
5629%      \dodoglobal\m_syst_helpers_scratch\m_syst_helpers_scratch
5630%    \fi}
5631%
5632% \def\syst_helpers_prepend_toks_indeed
5633%   {\tokspre\m_syst_helpers_scratch\t_syst_helpers_scratch
5634%    \ifx\dodoglobal\relax\else
5635%      \dodoglobal\m_syst_helpers_scratch\m_syst_helpers_scratch
5636%    \fi}
5637
5638\protected\def\appendtoks#1\to#2%
5639  {\ifx\dodoglobal\relax
5640     \expandafter\toksapp
5641   \else
5642     \resetglobal
5643     \expandafter\gtoksapp
5644   \fi#2{#1}}
5645
5646\protected\def\prependtoks#1\to#2%
5647  {\ifx\dodoglobal\relax
5648     \expandafter\tokspre
5649   \else
5650     \resetglobal
5651     \expandafter\gtokspre
5652   \fi#2{#1}}
5653
5654\def\syst_helpers_append_toks_indeed
5655  {\ifx\dodoglobal\relax
5656     \expandafter\toksapp
5657   \else
5658     \resetglobal
5659     \expandafter\gtoksapp
5660   \fi\m_syst_helpers_scratch\t_syst_helpers_scratch}
5661
5662\def\syst_helpers_prepend_toks_indeed
5663  {\ifx\dodoglobal\relax
5664     \expandafter\tokspre
5665   \else
5666     \resetglobal
5667     \expandafter\gtokspre
5668   \fi\m_syst_helpers_scratch\t_syst_helpers_scratch}
5669
5670\protected\def\appendtoksonce#1\to#2%
5671  {\let\m_syst_helpers_scratch#2%
5672   \t_syst_helpers_scratch{#1}%
5673   \doifelseintoks\t_syst_helpers_scratch\m_syst_helpers_scratch
5674     \donothing
5675     \syst_helpers_append_toks_indeed}
5676
5677\protected\def\prependtoksonce#1\to#2%
5678  {\let\m_syst_helpers_scratch#2%
5679   \t_syst_helpers_scratch{#1}%
5680   \doifelseintoks\t_syst_helpers_scratch\m_syst_helpers_scratch
5681     \donothing
5682     \syst_helpers_prepend_toks_indeed}
5683
5684%D The test macro:
5685
5686\protected\def\doifelseintoks#1#2% #1 en #2 zijn toks
5687  {\edef\asciia{\detokenize\expandafter{\the#1}}%
5688   \edef\asciib{\detokenize\expandafter{\the#2}}%
5689   \doifelsestringinstring\asciia\asciib}
5690
5691\let\doifintokselse\doifelseintoks
5692
5693%D Moved from \type {lxml-ini.tex} to here. This one is for generators that collect
5694%D stuff piecewise, which is sometimes hard on mechanisms that grab content using
5695%D delimiters:
5696%D
5697%D \starttyping
5698%D \startcollecting
5699%D \startcollect \bTABLE \stopcollect
5700%D     \startcollect \bTR \stopcollect
5701%D         \startcollect \bTD \stopcollect
5702%D         \startcollect   foo\stopcollect
5703%D         \startcollect \eTD \stopcollect
5704%D         \startcollect \bTD \stopcollect
5705%D         \startcollect   bar\stopcollect
5706%D         \startcollect \eTD \stopcollect
5707%D     \startcollect \eTR \stopcollect
5708%D \startcollect \eTABLE \stopcollect
5709%D \stopcollecting
5710%D \stoptyping
5711
5712\newtoks \collectingtoks
5713
5714\protected\def\startcollect        #1\stopcollect        {\toksapp \collectingtoks{#1}}
5715\protected\def\startexpandedcollect#1\stopexpandedcollect{\etoksapp\collectingtoks{#1}}
5716
5717\protected\def\startcollecting{\collectingtoks\emptytoks}
5718\protected\def\stopcollecting {\the\collectingtoks}
5719
5720\protected\def\collect        {\toksapp \collectingtoks}
5721\protected\def\collectexpanded{\etoksapp\collectingtoks}
5722
5723%D A nice one too:
5724
5725% {\scratchtoks{abc} \removetoks b\from\scratchtoks [\the\scratchtoks]}
5726% {\scratchtoks{abc} \removetoks x\from\scratchtoks [\the\scratchtoks]}
5727% {\scratchtoks{} \removetoks x\from\scratchtoks [\the\scratchtoks]}
5728% {\scratchtoks{xaa} \removetoks x\from\scratchtoks [\the\scratchtoks]}
5729% {\scratchtoks{a\relax b} \removetoks \relax\from\scratchtoks [\showthe\scratchtoks]}
5730
5731\protected\def\removetoks#1\from#2%
5732  {\def\syst_helpers_remove_toks##1#1##2\empty\empty\empty##3^^^^0004%
5733     {\def\m_syst_string_one{##3}%
5734      \ifx\m_syst_string_one\empty#2{##1}\else#2{##1##2}\fi}%
5735   \expandafter\syst_helpers_remove_toks\the#2\empty\empty\empty#1\empty\empty\empty^^^^0004}
5736
5737%D Also:
5738
5739% \protected\def\appendetoks #1\to{\normalexpanded{\appendtoks #1}\to}
5740% \protected\def\prependetoks#1\to{\normalexpanded{\prependtoks#1}\to}
5741
5742% \protected\def\appendetoks#1\to#2%
5743%   {\etoksapp#2{#1}%
5744%    \ifx\dodoglobal\relax\else
5745%      \global#2#2%
5746%    \fi}
5747%
5748% \protected\def\prependetoks#1\to#2%
5749%   {\etokspre#2{#1}%
5750%    \ifx\dodoglobal\relax\else
5751%       \global#2#2%
5752%    \fi}
5753
5754\protected\def\appendetoks#1\to#2%
5755  {\ifx\dodoglobal\relax
5756     \expandafter\etoksapp
5757   \else
5758     \resetglobal
5759     \expandafter\xtoksapp
5760   \fi#2{#1}}
5761
5762\protected\def\prependetoks#1\to#2%
5763  {\ifx\dodoglobal\relax
5764     \expandafter\etokspre
5765   \else
5766     \resetglobal
5767     \expandafter\xtokspre
5768   \fi#2{#1}}
5769
5770%D Hm.
5771
5772\protected\def\flushtoks#1% nb: can reassign to #1 again, hence the indirectness
5773  {\t_syst_helpers_scratch#1\relax
5774   \dodoglobal#1\emptytoks
5775   \the\t_syst_helpers_scratch\relax}
5776
5777% better: \def\flushtoks#1{\normalexpanded{\noexpand\dodoglobal#1\emptytoks\the#\relax}}
5778
5779\let\dotoks\the
5780
5781%D \macros
5782%D   {beforesplitstring,aftersplitstring}
5783%D
5784%D These both commands split a string at a given point in two
5785%D parts, so \type{x.y} becomes \type{x} or \type{y}.
5786%D
5787%D \starttyping
5788%D \beforesplitstring test.tex\at.\to\filename
5789%D \aftersplitstring  test.tex\at.\to\extension
5790%D \stoptyping
5791%D
5792%D The first routine looks (and is indeed) a bit simpler than the second one. The
5793%D alternative looking more or less like the first one did not always give the
5794%D results we needed. Both implementations show some insight in the manipulation of
5795%D arguments.
5796
5797\let\syst_helpers_split_string\relax
5798
5799\protected\def\beforesplitstring#1\at#2\to#3%
5800  {\def\syst_helpers_split_string##1#2##2#2##3\\%
5801     {\def#3{##1}}%
5802   \expandafter\syst_helpers_split_string#1#2#2\\}
5803
5804\protected\def\aftersplitstring#1\at#2\to#3%
5805  {\def\syst_helpers_split_string##1#2##2@@@##3\\%
5806     {\def#3{##2}}%
5807   \expandafter\syst_helpers_split_string#1@@@#2@@@\\}
5808
5809%D \macros
5810%D   {splitstring,greedysplitstring}
5811%D
5812%D A bonus macro.
5813
5814\protected\def\splitstring#1\at#2\to#3\and#4%
5815  {\def\syst_helpers_split_string##1#2##2\empty\empty\empty##3\\%
5816     {\def#3{##1}%
5817      \def\syst_helpers_split_string{##3}%
5818      \ifx\syst_helpers_split_string\empty
5819        \let#4\empty
5820      \else
5821        \def#4{##2}%
5822      \fi}%
5823   \expandafter\syst_helpers_split_string#1\empty\empty\empty#2\empty\empty\empty\\}
5824
5825\protected\def\greedysplitstring#1\at#2\to#3\and#4%
5826  {\edef\asciib{#1}%
5827   \let\asciic\asciib
5828   \let#3\empty
5829   \let#4\empty
5830   \doloop
5831     {\expandafter\splitstring\asciib\at#2\to\asciia\and\asciib
5832      \ifx\asciib\empty
5833        \exitloop
5834      \else
5835        % not \edef#3{\ifx#3\empty\else#3#2\fi\asciia} else
5836        % /root/path fails because then #3==empty
5837        \edef#3{\ifcase\recurselevel\or\else#3#2\fi\asciia}%
5838        \let#4\asciib
5839      \fi}%
5840  \ifx#3\empty\let#3\asciic\fi}
5841
5842%D \macros
5843%D   {beforetestandsplitstring,
5844%D    aftertestandsplitstring,
5845%D    testandsplitstring}
5846
5847\protected\def\beforetestandsplitstring#1\at#2\to#3%
5848  {\def\syst_helpers_split_string##1#2##2#2##3##4\\%
5849     {\ifx##3\empty\let#3\empty\else\def#3{##1}\fi}%
5850   \expandafter\syst_helpers_split_string#1#2#2\empty\\}
5851
5852\protected\def\aftertestandsplitstring#1\at#2\to#3%
5853  {\def\syst_helpers_split_string ##1#2##2@@@##3##4\\%
5854     {\ifx##3\empty\let#3\empty\else\def#3{##2}\fi}%
5855   \expandafter\syst_helpers_split_string #1@@@#2@@@\empty\\}
5856
5857\def\testandsplitstring#1\at#2\to#3\and#4%
5858  {\def\syst_helpers_split_string##1#2##2#2##3##4\\%
5859     {\ifx##3\empty\let#3\empty\let#4\empty\else\def#3{##1}\def#4{##2}\fi}%
5860   \expandafter\syst_helpers_split_string#1#2#2\empty\\}
5861
5862%D \macros
5863%D   {splitatperiod,
5864%D   {splitatcomma,
5865%D    splitatasterisk,
5866%D    splitatcolon,
5867%D    splitatcolons}
5868
5869\protected\def\splitatperiod  #1{\normalexpanded{\syst_helpers_splitatperiod  #1}..\relax}
5870\protected\def\splitatcomma   #1{\normalexpanded{\syst_helpers_splitatcomma   #1},,\relax}    % not at ", "
5871\protected\def\splitatasterisk#1{\normalexpanded{\syst_helpers_splitatasterisk#1}**\relax}
5872\protected\def\splitatcolon   #1{\normalexpanded{\syst_helpers_splitatcolon   #1}::\relax}
5873\protected\def\splitatcolons  #1{\normalexpanded{\syst_helpers_splitatcolons  #1}::::\relax}
5874
5875\protected\def\syst_helpers_splitatperiod    #1.#2.#3\relax#4#5{\def#4{#1}\def#5{#2}}
5876\protected\def\syst_helpers_splitatcomma     #1,#2,#3\relax#4#5{\def#4{#1}\def#5{#2}}
5877\protected\def\syst_helpers_splitatasterisk  #1*#2*#3\relax#4#5{\def#4{#1}\def#5{#2}}
5878\protected\def\syst_helpers_splitatcolon     #1:#2:#3\relax#4#5{\def#4{#1}\def#5{#2}}
5879\protected\def\syst_helpers_splitatcolons  #1::#2::#3\relax#4#5{\edef#4{#1}\edef#5{#2}}
5880
5881%D \macros
5882%D   {removesubstring}
5883%D
5884%D A first application of the two routines defined above is:
5885%D
5886%D \starttyping
5887%D \removesubstring-\from first-last\to\nothyphenated
5888%D \stoptyping
5889%D
5890%D Which in terms of \TEX\ looks like:
5891
5892\protected\def\removesubstring#1\from#2\to#3%
5893  {\splitstring#2\to\m_syst_string_one\and\m_syst_string_two
5894   \dodoglobal#3{\m_syst_string_one\m_syst_string_two}}
5895
5896%D \macros
5897%D   {appendtocommalist,prependtocommalist,
5898%D    addtocommalist,removefromcommalist}
5899%D
5900%D When working with comma separated lists, one sooner or later want the tools to
5901%D append or remove items from such a list. When we add an item, we first check if
5902%D it's already there. This means that every item in the list is unique.
5903%D
5904%D \starttyping
5905%D \addtocommalist      {alfa}  \name
5906%D \addtocommalist      {beta}  \name
5907%D \addtocommalist      {gamma} \name
5908%D \removefromcommalist {beta}  \name
5909%D \stoptyping
5910%D
5911%D These commands can be prefixed with \type {\doglobal}. The implementation of the
5912%D second command is more complecated, because we have to take leading spaces into
5913%D account. Keep in mind that users may provide lists with spaces after the commas.
5914%D When one item is left, we also have to get rid of trailing spaces.
5915%D
5916%D \starttyping
5917%D \def\words{alfa, beta, gamma, delta}
5918%D \def\words{alfa,beta,gamma,delta}
5919%D \stoptyping
5920%D
5921%D Removing an item takes more time than adding one. A fast appending alternative,
5922%D without any testing, is also provided:
5923%D
5924%D \starttyping
5925%D \appendtocommalist  {something} \name
5926%D \prependtocommalist {something} \name
5927%D \stoptyping
5928%D
5929%D This can be implemented as follows:
5930%D
5931%D \starttyping
5932%D \def\appendtocommalist#1#2%
5933%D   {\ifx#2\empty
5934%D      \dodoglobal\edef#2{#1}%
5935%D    \else % no test on empty
5936%D      \dodoglobal\edef#2{#2,#1}%
5937%D    \fi}
5938%D
5939%D \def\prependtocommalist#1#2%
5940%D   {\ifx#2\empty
5941%D      \dodoglobal\edef#2{#1}%
5942%D    \else % no test on empty
5943%D      \dodoglobal\edef#2{#1,#2}%
5944%D    \fi}
5945%D \stoptyping
5946%D
5947%D The faster alternatives are:
5948
5949\protected\def\appendtocommalist#1#2%
5950  {\dodoglobal\edef#2{\ifx#2\empty\else#2,\fi#1}}
5951
5952\protected\def\prependtocommalist#1#2%
5953  {\dodoglobal\edef#2{#1\ifx#2\empty\else,#2\fi}}
5954
5955\protected\def\addtocommalist#1#2% {item} \cs
5956  {\rawdoifelseinset{#1}#2\resetglobal
5957     {\dodoglobal\edef#2{\ifx#2\empty\else#2,\fi#1}}}
5958
5959\protected\def\pretocommalist#1#2% {item} \cs
5960  {\rawdoifelseinset{#1}#2\resetglobal
5961     {\dodoglobal\edef#2{#1\ifx#2\empty\else,#2\fi}}}
5962
5963\protected\def\robustdoifelseinset#1#2%
5964  {\edef\m_syst_string_one{\detokenize\expandafter{\normalexpanded{#1}}}%
5965   \edef\m_syst_string_two{\detokenize\expandafter{\normalexpanded{#2}}}%
5966   \rawdoifelseinset\m_syst_string_one\m_syst_string_two}
5967
5968\let\robustdoifinsetelse\robustdoifelseinset
5969
5970\protected\def\robustaddtocommalist#1#2% {item} \cs
5971  {\robustdoifelseinset{#1}#2\resetglobal
5972     {\dodoglobal\edef#2{\ifx#2\empty\else#2,\fi#1}}}
5973
5974\protected\def\robustpretocommalist#1#2% {item} \cs
5975  {\robustdoifelseinset{#1}#2\resetglobal
5976     {\dodoglobal\edef#2{#1\ifx#2\empty\else,#2\fi}}}
5977
5978\protected\def\xsplitstring#1#2% \cs {str}
5979  {\def\syst_helpers_split_string##1,#2,##2,#2,##3\\%
5980     {\edef\m_syst_string_one{\bcleanedupcommalist##1\empty\empty\relax}%
5981      \edef\m_syst_string_two{\acleanedupcommalist##2,,\relax}}%
5982   \expandafter\syst_helpers_split_string\expandafter,#1,,#2,,#2,\\}
5983
5984\def\bcleanedupcommalist#1#2#3\relax{\if#1,\else#1\fi\if#2,\else#2\fi#3}
5985\def\bcleanedupcommalist#1#2\relax{\if#1,\else#1\fi#2}
5986\def\acleanedupcommalist#1,,#2\relax{#1}
5987
5988\protected\def\removefromcommalist#1#2% to be sped up
5989  {\rawdoifelseinset{#1}#2%
5990     {\normalexpanded{\xsplitstring\noexpand#2{#1}}%
5991      \dodoglobal\edef#2%
5992        {\ifx\m_syst_string_one\empty
5993           \m_syst_string_two
5994         \else
5995           \m_syst_string_one\ifx\m_syst_string_two\empty\else,\m_syst_string_two\fi
5996         \fi}}
5997     \resetglobal}
5998
5999% \protected\def\addtocommalist#1#2% upto 3 times slower
6000%   {\dodoglobal\edef#2{\ctxcommand{addtocommalist(\!!bs#1\!!es,\!!bs#2\!!es)}}}
6001%
6002% \protected\def\removefromcommalist#1#2% faster and more robust
6003%   {\dodoglobal\edef#2{\ctxcommand{addtocommalist(\!!bs#1\!!es,\!!bs#2\!!es)}}}
6004
6005%D \macros
6006%D   {substituteincommalist}
6007%D
6008%D Slow but seldom used, so for the moment we stick to this implementation.
6009%D
6010%D \starttyping
6011%D \substituteincommalist{old}{new}{list}
6012%D \stoptyping
6013
6014\def\syst_helpers_substitute_in_comma_list_step#1%
6015  {\edef\m_syst_string_three{#1}%
6016   \ifx\m_syst_string_one\m_syst_string_three
6017     \ifx\m_syst_string_two\empty \else
6018       \edef\m_syst_string_four{\ifx\m_syst_string_four\empty\else\m_syst_string_four,\fi\m_syst_string_two}%
6019     \fi
6020   \else
6021     \edef\m_syst_string_four{\ifx\m_syst_string_four\empty\else\m_syst_string_four,\fi#1}%
6022   \fi}
6023
6024\protected\def\substituteincommalist#1#2#3% old, new, list (slooow)
6025  {\edef\m_syst_string_one{#1}%
6026   \edef\m_syst_string_two{#2}%
6027   \let\m_syst_string_four\empty
6028   \normalexpanded{\rawprocesscommacommand[#3]}\syst_helpers_substitute_in_comma_list_step
6029   \let#3\m_syst_string_four}
6030
6031%D \macros
6032%D   {replaceincommalist}
6033%D
6034%D The next macro can be used to replace an indexed element in a commalist:
6035%D
6036%D \starttyping
6037%D \replaceincommalist\MyList{2}
6038%D \stoptyping
6039%D
6040%D Element~2 will be replaced by the current meaning of the macro \type
6041%D {\newcommalistelement}. The old meaning is saved in \type {\commalistelement}.
6042%D The replacement honors grouped items, like in:
6043%D
6044%D \starttyping
6045%D \def\MyList{a,b,c,d,e,f}   \replaceincommalist\MyList{3}
6046%D \def\MyList{a,b,c,d,e,f}   \replaceincommalist\MyList{3}
6047%D \def\MyList{a,{b,c},d,e,f} \replaceincommalist\MyList{3}
6048%D \def\MyList{a,b,c,{d,e,f}} \replaceincommalist\MyList{3}
6049%D \stoptyping
6050%D
6051%D This macro was used in the bibtex code (and is probably no longer needed).
6052
6053\newcount\c_syst_helpers_comma_list_index
6054\let     \m_syst_helpers_comma_list_target\empty
6055
6056\let\newcommalistelement\empty
6057
6058\def\syst_helpers_replace_in_comma_list_step#1%
6059  {\ifnum\commalistcounter=\c_syst_helpers_comma_list_index\relax
6060     \ifx\newcommalistelement\empty\else
6061       \ifx\m_syst_helpers_comma_list_target\empty
6062         \let\m_syst_helpers_comma_list_target\newcommalistelement
6063       \else
6064         \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter
6065           \m_syst_helpers_comma_list_target\expandafter\expandafter\expandafter
6066             {\expandafter\m_syst_helpers_comma_list_target\expandafter,\newcommalistelement}%
6067       \fi
6068     \fi
6069     \def\commalistelement{#1}%
6070   \else
6071     \ifx\m_syst_helpers_comma_list_target\empty
6072       \ifx\nexttoken\bgroup % is known -)
6073         \def\m_syst_helpers_comma_list_target{{#1}}%
6074       \else
6075         \def\m_syst_helpers_comma_list_target{#1}%
6076       \fi
6077     \else
6078       \ifx\nexttoken\bgroup % is known -)
6079         \expandafter\def\expandafter\m_syst_helpers_comma_list_target\expandafter{\m_syst_helpers_comma_list_target,{#1}}%
6080       \else
6081         \expandafter\def\expandafter\m_syst_helpers_comma_list_target\expandafter{\m_syst_helpers_comma_list_target,#1}%
6082       \fi
6083     \fi
6084   \fi
6085   \advance\commalistcounter\plusone}
6086
6087\protected\def\replaceincommalist#1#2% #1 = commalistelement #2 = position starts at 1
6088  {\c_syst_helpers_comma_list_index#2\relax
6089   \let\m_syst_helpers_comma_list_target\empty
6090   \let\commalistelement\empty
6091   \commalistcounter\plusone
6092   \expandafter\processcommalist\expandafter[#1]\syst_helpers_replace_in_comma_list_step
6093   \dodoglobal\let#1\m_syst_helpers_comma_list_target}
6094
6095%D \macros
6096%D   {globalprocesscommalist}
6097%D
6098%D The commalist processing commands are characterized by the fact that the way they
6099%D handle expansion as well as the fact that they can be nested. This makes them
6100%D kind of useless for handling comma lists in alignments. In these situations the
6101%D next macro can be of use.
6102
6103\let\m_syst_helpers_comma_list_command_global\empty
6104
6105\def\syst_helpers_comma_list_command_global_step#1,%
6106  {\if]#1\else
6107     \m_syst_helpers_comma_list_command_global{#1}%
6108     \expandafter\syst_helpers_comma_list_command_global_step
6109   \fi}
6110
6111\protected\def\globalprocesscommalist[#1]#2%
6112  {\glet\m_syst_helpers_comma_list_command_global#2%
6113   \expandafter\syst_helpers_comma_list_command_global_step#1,],}
6114
6115%D \macros
6116%D   {withoutpt,PtToCm,
6117%D    numberofpoints,dimensiontocount}
6118%D
6119%D We can convert point into centimeters with:
6120%D
6121%D \starttyping
6122%D \PtToCm{dimension}
6123%D \stoptyping
6124
6125{\catcode`\.=\othercatcode
6126 \catcode`\p=\othercatcode
6127 \catcode`\t=\othercatcode
6128 \gdef\WITHOUTPT#1pt{#1}}
6129
6130\def\withoutpt#1%
6131  {\expandafter\WITHOUTPT#1}
6132
6133%D The capitals are needed because \type {p} and \type {t} have catcode~12, while
6134%D macronames only permit tokens with the catcode~11. As a result we cannot use the
6135%D \type {.group} primitives. Those who want to know more about this kind of
6136%D manipulations, we advice to study the \TEX book in detail. Because this macro
6137%D does not do any assignment, we can use it in the following way too.
6138
6139\def\PtToCm#1%
6140  {\withoutpt\the\dimexpr0.0351459804\dimexpr#1\relax\relax cm}
6141
6142%D We also support:
6143%D
6144%D \starttyping
6145%D \numberofpoints   {dimension}
6146%D \dimensiontocount {dimension} {\count}
6147%D \stoptyping
6148%D
6149%D Both macros return a rounded number.
6150
6151% \dimensiontocount{10.49pt}\scratchcounter \the\scratchcounter / \numberofpoints{10.49pt}
6152% \dimensiontocount{10.51pt}\scratchcounter \the\scratchcounter / \numberofpoints{10.51pt}
6153
6154\def\dimensiontocount#1#2{#2\numexpr\dimexpr#1\relax/\maxcard\relax}
6155\def\numberofpoints    #1{\the\numexpr\dimexpr#1\relax/\maxcard\relax}
6156
6157%D \macros
6158%D   {swapdimens,swapskips,swapcounts,swapmacros,
6159%D    globalswapdimens,globalswapcounts,globalswapmacros}
6160%D
6161%D Simple but effective are the next two macros. There name exactly states their
6162%D purpose.
6163
6164\newdimen\d_syst_helpers_swapped
6165\newskip \s_syst_helpers_swapped
6166\newcount\c_syst_helpers_swapped
6167\let     \m_syst_helpers_swapped\relax
6168
6169\protected\def\swapdimens#1#2{\d_syst_helpers_swapped    #1\relax#1#2\relax#2\d_syst_helpers_swapped}
6170\protected\def\swapskips #1#2{\s_syst_helpers_swapped    #1\relax#1#2\relax#2\s_syst_helpers_swapped}
6171\protected\def\swapcounts#1#2{\c_syst_helpers_swapped    #1\relax#1#2\relax#2\c_syst_helpers_swapped}
6172\protected\def\swapmacros#1#2{\let\m_syst_helpers_swapped#1\let  #1#2\let  #2\m_syst_helpers_swapped}
6173
6174\protected\def\globalswapdimens#1#2{\d_syst_helpers_swapped    #1\global#1#2\global#2\d_syst_helpers_swapped}
6175\protected\def\globalswapskips #1#2{\s_syst_helpers_swapped    #1\global#1#2\global#2\s_syst_helpers_swapped}
6176\protected\def\globalswapcounts#1#2{\c_syst_helpers_swapped    #1\global#1#2\global#2\c_syst_helpers_swapped}
6177\protected\def\globalswapmacros#1#2{\let\m_syst_helpers_swapped#1\glet  #1#2\glet  #2\m_syst_helpers_swapped}
6178
6179%D \macros
6180%D   {pushmacro,popmacro}
6181%D
6182%D Premature and a bit of beta, we offer:
6183%D
6184%D \starttyping
6185%D \pushmacro\macro
6186%D \popmacro\macro
6187%D \stoptyping
6188%D
6189%D Beware: global!
6190
6191\installsystemnamespace{localpushedmacro}
6192\installsystemnamespace{globalpushedmacro}
6193
6194% \let\m_syst_helpers_push_macro\empty
6195%
6196% \protected\def\globalpushmacro#1%
6197%   {\xdef\m_syst_helpers_push_macro{\string#1}%
6198%    \ifcsname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname \else
6199%      \expandafter\newcount\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname
6200%    \fi
6201%    \global\advance\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname \plusone
6202%    \expandafter\glet\csname\the\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname\m_syst_helpers_push_macro\endcsname#1}
6203%
6204% \protected\def\globalpopmacro#1%
6205%   {\xdef\m_syst_helpers_push_macro{\string#1}%
6206%    \expandafter\glet\expandafter#1\csname\the\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname\m_syst_helpers_push_macro\endcsname
6207%    \global\advance\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname \minusone}
6208%
6209% \protected\def\localpushmacro#1% this one can be used to push a value over an \egroup
6210%   {\xdef\m_syst_helpers_push_macro{\string#1}%
6211%    \ifcsname\??localpushedmacro\m_syst_helpers_push_macro\endcsname \else
6212%      \expandafter\newcount\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname
6213%    \fi
6214%    \global\advance\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname \plusone
6215%    \expandafter\glet\csname\the\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname\m_syst_helpers_push_macro\endcsname#1}
6216%
6217% \protected\def\localpopmacro#1%
6218%   {\xdef\m_syst_helpers_push_macro{\string#1}%
6219%    \expandafter\let\expandafter#1\csname\the\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname\m_syst_helpers_push_macro\endcsname
6220%    \global\advance\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname \minusone }
6221%
6222% \let\pushmacro\localpushmacro
6223% \let\popmacro \localpopmacro
6224%
6225% slightly faster but more important: less tracing
6226%
6227% possible optimization \installmacrostack\foo: \syst_push_foo \syst_pop_foo
6228
6229\let\m_syst_helpers_push_macro\empty
6230
6231\newcount\c_syst_helpers_pop_count
6232
6233\def\syst_helpers_push_macro_new_global
6234  {\expandafter\newcount\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname
6235   \global\advance\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname\plusone}
6236
6237\def\syst_helpers_push_macro_new_local
6238  {\expandafter\newcount\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname
6239   \global\advance\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname\plusone}
6240
6241\protected\def\globalpushmacro#1%
6242  {\xdef\m_syst_helpers_push_macro{\csstring#1}%
6243   \ifcsname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname
6244     \global\advance\lastnamedcs\plusone
6245   \else
6246     \syst_helpers_push_macro_new_global
6247   \fi
6248   \expandafter\glet\csname\the\lastnamedcs\m_syst_helpers_push_macro\endcsname#1}
6249
6250\protected\def\localpushmacro#1% this one can be used to push a value over an \egroup
6251  {\xdef\m_syst_helpers_push_macro{\csstring#1}%
6252   \ifcsname\??localpushedmacro\m_syst_helpers_push_macro\endcsname
6253     \global\advance\lastnamedcs\plusone
6254   \else
6255     \syst_helpers_push_macro_new_local
6256   \fi
6257   \expandafter\glet\csname\the\lastnamedcs\m_syst_helpers_push_macro\endcsname#1}
6258
6259\protected\def\globalpopmacro#1%
6260  {\xdef\m_syst_helpers_push_macro{\csstring#1}%
6261   \c_syst_helpers_pop_count\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname
6262   \global\advance\lastnamedcs \minusone
6263   \expandafter\glet\expandafter#1\csname\the\c_syst_helpers_pop_count\m_syst_helpers_push_macro\endcsname}
6264
6265\protected\def\localpopmacro#1%
6266  {\xdef\m_syst_helpers_push_macro{\csstring#1}%
6267   \c_syst_helpers_pop_count\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname
6268   \global\advance\lastnamedcs \minusone
6269   \expandafter\let\expandafter#1\csname\the\c_syst_helpers_pop_count\m_syst_helpers_push_macro\endcsname}
6270
6271\let\pushmacro\localpushmacro
6272\let\popmacro \localpopmacro
6273
6274%D \macros
6275%D   {setlocalhsize,distributedhsize}
6276%D
6277%D Sometimes we need to work with the \type{ \hsize} that is corrected for
6278%D indentation and left and right skips. The corrected value is available in \type
6279%D {\localhsize}, which needs to be calculated with \type {\setlocalhsize} first. %D
6280%D
6281%D \starttyping
6282%D \setlocalhsize        \hbox to \localhsize{...}
6283%D \setlocalhsize[-1em]  \hbox to \localhsize{...}
6284%D \setlocalhsize[.5ex]  \hbox to \localhsize{...}
6285%D \stoptyping
6286%D
6287%D These examples show us that an optional can be used. The value provided is added
6288%D to \type {\localhsize}.
6289
6290\newdimen\localhsize
6291
6292\protected\def\setlocalhsize % don't change !
6293  {\doifelsenextoptional
6294     \syst_helpers_set_local_hsize_yes
6295     \syst_helpers_set_local_hsize_nop}
6296
6297\def\syst_helpers_set_local_hsize_nop
6298  {\localhsize\availablehsize}
6299
6300\def\syst_helpers_set_local_hsize_yes[#1]%
6301  {\syst_helpers_set_local_hsize_nop
6302   \advance\localhsize#1\relax}
6303
6304\def\availablehsize
6305  {\dimexpr
6306     \hsize-\leftskip-\rightskip
6307     \ifnum\hangafter<\zerocount
6308       \ifdim\hangindent>\zeropoint-\else+\fi\hangindent
6309     \fi
6310   \relax}
6311
6312\def\distributedhsize#1#2#3%
6313  {\dimexpr(#1-\numexpr#3-1\relax\dimexpr#2\relax)/#3\relax}
6314
6315\def\hsizefraction#1#2%
6316  {\dimexpr#1/#2\relax}
6317
6318%D \macros
6319%D   {doifvalue,doifnotvalue,doifelsevalue,
6320%D    doifnothing,doifsomething,doifelsenothing,
6321%D    doifvaluenothing,doifvaluesomething,doifelsevaluenothing}
6322%D
6323%D These \type {\if} commands can be used to access macros (or variables) that are
6324%D normally accessed by using \type {\getvalue}. Using these alternatives safes us
6325%D three tokens per call. Anyone familiar with the not||values ones, can derive
6326%D their meaning from the definitions.
6327
6328\protected\def\doifvalue#1#2%
6329  {\edef\m_syst_string_one{\csname#1\endcsname}%
6330   \edef\m_syst_string_two{#2}%
6331   \ifx\m_syst_string_one\m_syst_string_two
6332     \expandafter\firstofoneargument
6333   \else
6334     \expandafter\gobbleoneargument
6335   \fi}
6336
6337\protected\def\doifnotvalue#1#2%
6338  {\edef\m_syst_string_one{\csname#1\endcsname}%
6339   \edef\m_syst_string_two{#2}%
6340   \ifx\m_syst_string_one\m_syst_string_two
6341     \expandafter\gobbleoneargument
6342   \else
6343     \expandafter\firstofoneargument
6344   \fi}
6345
6346\protected\def\doifelsevalue#1#2%
6347  {\edef\m_syst_string_one{\csname#1\endcsname}%
6348   \edef\m_syst_string_two{#2}%
6349   \ifx\m_syst_string_one\m_syst_string_two
6350     \expandafter\firstoftwoarguments
6351   \else
6352     \expandafter\secondoftwoarguments
6353   \fi}
6354
6355\protected\def\doifnothing#1%
6356  {\edef\m_syst_string_one{#1}%
6357   \ifx\m_syst_string_one\empty
6358     \expandafter\firstofoneargument
6359   \else
6360     \expandafter\gobbleoneargument
6361   \fi}
6362
6363\protected\def\doifsomething#1%
6364  {\edef\m_syst_string_one{#1}%
6365   \ifx\m_syst_string_one\empty
6366     \expandafter\gobbleoneargument
6367   \else
6368     \expandafter\firstofoneargument
6369   \fi}
6370
6371\protected\def\doifelsenothing#1%
6372  {\edef\m_syst_string_one{#1}%
6373   \ifx\m_syst_string_one\empty
6374     \expandafter\firstoftwoarguments
6375   \else
6376     \expandafter\secondoftwoarguments
6377   \fi}
6378
6379\protected\def\doifelsesomething#1%
6380  {\edef\m_syst_string_one{#1}%
6381   \ifx\m_syst_string_one\empty
6382     \expandafter\secondoftwoarguments
6383   \else
6384     \expandafter\firstoftwoarguments
6385   \fi}
6386
6387\protected\def\doifvaluenothing#1%
6388  {\edef\m_syst_string_one{\csname#1\endcsname}%
6389   \ifx\m_syst_string_one\empty
6390     \expandafter\firstofoneargument
6391   \else
6392     \expandafter\gobbleoneargument
6393   \fi}
6394
6395\protected\def\doifvaluesomething#1%
6396  {\edef\m_syst_string_one{\csname#1\endcsname}%
6397   \ifx\m_syst_string_one\empty
6398     \expandafter\gobbleoneargument
6399   \else
6400     \expandafter\firstofoneargument
6401   \fi}
6402
6403\protected\def\doifelsevaluenothing#1%
6404  {\edef\m_syst_string_one{\csname#1\endcsname}%
6405   \ifx\m_syst_string_one\empty
6406     \expandafter\firstoftwoarguments
6407   \else
6408     \expandafter\secondoftwoarguments
6409   \fi}
6410
6411\let\doifvalueelse       \doifelsevalue
6412\let\doifnothingelse     \doifelsenothing
6413\let\doifsomethingelse   \doifelsesomething
6414\let\doifvaluenothingelse\doifelsevaluenothing
6415
6416%D \macros
6417%D   {doifemptyelsevalue, doifemptyvalue, doifnotemptyvalue}
6418%D
6419%D Also handy:
6420
6421\def\doifelseemptyvalue#1%
6422  {\expandafter\ifx\csname#1\endcsname\empty
6423     \expandafter\firstoftwoarguments
6424   \else
6425     \expandafter\secondoftwoarguments
6426   \fi}
6427
6428\let\doifemptyvalueelse\doifelseemptyvalue
6429
6430\def\doifemptyvalue#1%
6431  {\expandafter\ifx\csname#1\endcsname\empty
6432     \expandafter\firstofoneargument
6433   \else
6434     \expandafter\gobbleoneargument
6435   \fi}
6436
6437\def\doifnotemptyvalue#1%
6438  {\expandafter\ifx\csname#1\endcsname\empty
6439     \expandafter\gobbleoneargument
6440   \else
6441     \expandafter\firstofoneargument
6442   \fi}
6443
6444%D \macros
6445%D   {doifallcommonelse}
6446%D
6447%D A complete match of two sets can be tested with \type {\doifallcommonelse}, where
6448%D the first two arguments are sets.
6449
6450\def\syst_helpers_do_if_all_common_else#1#2#3#4% slow
6451  {\def\syst_helpers_do_common_check_all##1%
6452     {\doifnotinset{##1}{#4}\donefalse
6453      \ifdone\else\expandafter\quitcommalist\fi}%
6454   \donetrue
6455   \processcommalist[#3]\syst_helpers_do_common_check_all
6456   \ifdone\expandafter#1\else\expandafter#2\fi}
6457
6458\protected\def\doifelseallcommon{\syst_helpers_do_if_all_common_else\firstoftwoarguments\secondoftwoarguments}
6459\protected\def\doifallcommon    {\syst_helpers_do_if_all_common_else\firstofonearguments\gobbleoneargument   }
6460\protected\def\doifnotallcommon {\syst_helpers_do_if_all_common_else\gobbleoneargument  \firstofonearguments }
6461
6462\let\doifallcommonelse\doifelseallcommon
6463
6464%D \macros
6465%D   {DOIF,DOIFELSE,DOIFNOT}
6466%D
6467%D \TEX\ is case sensitive. When comparing arguments, this feature sometimes is less
6468%D desirable, for instance when we compare filenames. The next three alternatives
6469%D upcase their arguments before comparing them.
6470%D
6471%D \starttyping
6472%D \DOIF     {string1} {string2} {...}
6473%D \DOIFNOT  {string1} {string2} {...}
6474%D \DOIFELSE {string1} {string2} {then ...}{else ...}
6475%D \stoptyping
6476%D
6477%D We have to use a two||step implementation, because the
6478%D expansion has to take place outside \type{\uppercase}.
6479%D
6480%D These might end up as \LUA based helpers (i.e. consider these
6481%D obsolete:
6482
6483\protected\def\syst_helpers_do_IF#1#2%
6484  {\uppercase{\syst_helpers_do_if_in_string_else{$#1$}{$#2$}}%
6485     \expandafter\firstofoneargument
6486   \else
6487     \expandafter\gobbleoneargument
6488   \fi}
6489
6490\protected\def\syst_helpers_do_IF_NOT#1#2%
6491  {\uppercase{\syst_helpers_do_if_in_string_else{$#1$}{$#2$}}%
6492     \expandafter\gobbleoneargument
6493   \else
6494     \expandafter\firstofoneargument
6495   \fi}
6496
6497\protected\def\syst_helpers_do_IF_ELSE#1#2%
6498  {\uppercase{\syst_helpers_do_if_in_string_else{$#1$}{$#2$}}%
6499     \expandafter\firstoftwoarguments
6500   \else
6501     \expandafter\secondoftwoarguments
6502   \fi}
6503
6504\protected\def\syst_helpers_do_IF_INSTRING_ELSE#1#2%
6505  {\uppercase{\syst_helpers_do_if_in_string_else{$#1$}{$#2$}}%
6506     \expandafter\firstoftwoarguments
6507   \else
6508     \expandafter\secondoftwoarguments
6509   \fi}
6510
6511\protected\def\DOIF             #1#2{\normalexpanded{\syst_helpers_do_IF              {#1}{#2}}}% will become obsolete
6512\protected\def\DOIFNOT          #1#2{\normalexpanded{\syst_helpers_do_IF_NOT          {#1}{#2}}}% will become obsolete
6513\protected\def\DOIFELSE         #1#2{\normalexpanded{\syst_helpers_do_IF_ELSE         {#1}{#2}}}% will become obsolete
6514\protected\def\DOIFINSTRINGELSE #1#2{\normalexpanded{\syst_helpers_do_IF_INSTRING_ELSE{#1}{#2}}}% will become obsolete
6515
6516%D \macros
6517%D   {dosingleargumentwithset,
6518%D    dodoubleargumentwithset,dodoubleemptywithset,
6519%D    dotripleargumentwithset,dotripleemptywithset}
6520%D
6521%D These maybe too mysterious macros enable us to handle more than one setup at once.
6522%D
6523%D \starttyping
6524%D \dosingleargumentwithset \command[#1]
6525%D \dodoubleargumentwithset \command[#1][#2]
6526%D \dotripleargumentwithset \command[#1][#2][#3]
6527%D \dodoubleemptywithset    \command[#1][#2]
6528%D \dotripleemptywithset    \command[#1][#2][#3]
6529%D \stoptyping
6530%D
6531%D The first macro calls \type {\command[##1]} for each string in the set~\type
6532%D {#1}. The second one calls for \typ {\command [##1][#2]} and the third, well one
6533%D may guess. These commands support constructions like:
6534%D
6535%D \starttyping
6536%D \def\dodefinesomething[#1][#2]%
6537%D   {\getparameters[\??xx#1][#2]}
6538%D
6539%D \protected\def\definesomething%
6540%D   {\dodoubleargumentwithset\dodefinesomething}
6541%D \stoptyping
6542%D
6543%D Which accepts calls like:
6544%D
6545%D \starttyping
6546%D \definesomething[alfa,beta,...][variable=...,...]
6547%D \stoptyping
6548
6549\let\m_syst_helpers_with_set_command\empty
6550\let\syst_helpers_with_set_step     \relax
6551
6552\def\syst_helpers_with_set_double[#1][#2]%
6553  {\doifsomething{#1}%
6554     {\def\syst_helpers_with_set_step##1{\m_syst_helpers_with_set_command[##1][#2]}%
6555      \processcommalist[#1]\syst_helpers_with_set_step}}
6556
6557\def\syst_helpers_with_set_triple[#1][#2][#3]%
6558  {\doifsomething{#1}%
6559     {\def\syst_helpers_with_set_step##1{\m_syst_helpers_with_set_command[##1][#2][#3]}%
6560      \processcommalist[#1]\syst_helpers_with_set_step}}
6561
6562\def\dodoubleemptywithset   #1{\let\m_syst_helpers_with_set_command#1\dodoubleempty   \syst_helpers_with_set_double} % \command
6563\def\dodoubleargumentwithset#1{\let\m_syst_helpers_with_set_command#1\dodoubleargument\syst_helpers_with_set_double} % \command
6564
6565\def\dotripleemptywithset   #1{\let\m_syst_helpers_with_set_command#1\dotripleempty   \syst_helpers_with_set_triple} % \command
6566\def\dotripleargumentwithset#1{\let\m_syst_helpers_with_set_command#1\dotripleargument\syst_helpers_with_set_triple} % \command
6567
6568%D \macros
6569%D   {stripcharacters,stripspaces}
6570%D
6571%D The next command was needed first when we implemented the \CONTEXT\ interactivity
6572%D macros. When we use labeled destinations, we often cannot use all the characters
6573%D we want. We therefore strip some of the troublemakers, like spaces, from the