syst-aux.mkxl /size: 234 Kb    last modification: 2024-01-16 09:03
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%D In the process of being adapted to \LMTX\ flags so a bit of a mess now. Also
15%D our playground.
16
17\registerctxluafile{syst-aux}{autosuffix}
18\registerctxluafile{syst-mac}{autosuffix}
19
20%D This file is a follow up in \type {syst-aux.mkii} and \type {syst-aux.mkiv} where
21%D you can find some more pure \TEX\ or \LUATEX\ variants. There are quite some
22%D helpers here and many no longer are used but we keep them around because they
23%D have always been there. However, the implementation in \LMTX\ can be somewhat
24%D different that in \MKIV, because it's also a kind of a playground for new
25%D features. Of course nostalgia also plays a role.
26%D
27%D For the original code we refer to \type {syst-aux.mkii} and its follow up \type
28%D {syst-aux.mkiv}. These also have historic versions. As usual, the upgrade went
29%D through stages. First I wrote variants usign the new \type {\ignorearguments}
30%D feature, then I decided to implement a few more conditionals and new fancy code
31%D could be ditched before it was even published. I kept some as examples.
32%D
33%D Of course I couldn't do this without Wolfgang looking over my shoulder. Changing
34%D core code is kind of tricky.
35%D
36%D There are quite some helpers here that are effectively obsolete (read: there is no
37%D need for them). Examples are \type {setflag} and \type {\uppercasestring} and their
38%D relatives. We keep them around as illustration and possible usage in old styles.
39
40\unprotect
41
42%D These are for local helpers, the names are historic:
43
44\mutable\let\docommand  \relax
45\mutable\let\dodocommand\relax
46
47%D \macros
48%D   {unexpanded}
49%D
50%D Because we use this module only in \MKIV, we have removed the old protection
51%D code.
52%D
53%D \starttyping
54%D \protected\def\somecommand{... ... ...}
55%D \stoptyping
56%D
57%D This overloads the \ETEX\ primitive but as we already had an \MKII\ solution we
58%D keep the same name for a similar mechanism. So, effectively we have two ways to
59%D protect a macro.
60%D
61%D In \LMTX\ we try to avoid this and use \type {\protected} instead, which is
62%D possible because over time we got rid of using the \CONTEXT\ macro with that
63%D name.
64
65\pushoverloadmode
66
67\aliased\let\unexpanded\normalprotected
68
69\popoverloadmode
70
71%D We're definitely in \LMTX\ mode here.
72
73\permanent\protected\lettonothing\startlmtxmode
74\permanent\protected\lettonothing\stoplmtxmode
75\permanent                   \def\startmkivmode#-\stopmkivmode{}
76\permanent\protected\lettonothing\stopmkivmode
77
78%D As we don't have namespace definers yet, we use a special one. Later we will
79%D do a better job.
80
81\ifdefined\c_syst_helpers_n_of_namespaces
82
83    % lets plug in a better error message
84
85\else
86
87    \newinteger\c_syst_helpers_n_of_namespaces \c_syst_helpers_n_of_namespaces\pluseight % 1-8 reserved for catcodes
88
89    \def\v_interfaces_prefix_template_system{\the       \c_syst_helpers_n_of_namespaces>>}
90    %def\v_interfaces_prefix_template_system{\characters\c_syst_helpers_n_of_namespaces>>} % no \characters yet
91
92\fi
93
94\permanent\protected\def\installsystemnamespace#1% maybe move this to syst-ini
95  {\ifcsname ??#1\endcsname
96     \writestatus\m!system{duplicate system namespace '#1'}\wait
97   \else
98     \global\advanceby\c_syst_helpers_n_of_namespaces\plusone
99     \immutable\edefcsname ??#1\endcsname{\v_interfaces_prefix_template_system}%
100   \fi}
101
102%D \macros
103%D   {normalspace}
104%D
105%D There is already \type{\space} but just to be sure we also provide this one:
106
107\permanent\def\normalspace{ }
108
109\newif\if!!donea   \newif\if!!doneb    \newif\if!!donec  % soon obsolete in lmtx
110\newif\if!!doned   \newif\if!!donee    \newif\if!!donef  % soon obsolete in lmtx
111
112\immutable\def\!!zerocount {0} % alongside \zerocount
113\immutable\def\!!minusone {-1} % ...
114\immutable\def\!!plusone   {1} % ...
115\immutable\def\!!plustwo   {2} % ...
116\immutable\def\!!plusthree {3} % ...
117\immutable\def\!!plusfour  {4} % ...
118\immutable\def\!!plusfive  {5} % ...
119\immutable\def\!!plussix   {6} % ...
120\immutable\def\!!plusseven {7} % ...
121\immutable\def\!!pluseight {8} % ...
122\immutable\def\!!plusnine  {9} % alongside \plusnine
123
124\setnewconstant   \uprotationangle    0
125\setnewconstant\rightrotationangle   90
126\setnewconstant \downrotationangle  180
127\setnewconstant \leftrotationangle  270
128
129%D \macros
130%D   {s!,c!,e!,p!,v!,@@,??}
131%D
132%D To save memory, we use constants (sometimes called variables). Redefining these
133%D constants can have disastrous results. Of course, expanding them costs time, so
134%D that's the price to pay. More are defined elsewhere. Of course we later pay a
135%D price when they need to be expanded.
136
137% \def\v!prefix! {v!}
138% \def\c!prefix! {c!}
139% \def\s!prefix! {s!}
140%
141% \def\s!next    {next}
142% \def\s!default {default}
143% \def\s!dummy   {dummy}
144% \def\s!unknown {unknown}
145%
146% \def\s!do      {do}
147% \def\s!dodo    {dodo}
148%
149% \def\s!complex {complex}
150% \def\s!simple  {simple}
151%
152% \def\s!start{start}
153% \def\s!stop {stop}
154
155% \immutable\def\s!empty {empty}
156
157%D Sometimes we pass macros as arguments to commands that don't expand them before
158%D interpretation. Such commands can be enclosed with \type {\expanded}, like:
159%D
160%D \starttyping
161%D \expanded{\setupsomething[\alfa]}
162%D \stoptyping
163%D
164%D Such situations occur for instance when \type {\alfa} is a commalist or when data
165%D stored in macros is fed to index of list commands. If needed, one should use
166%D \type {\noexpand} inside the argument. Later on we will meet some more clever
167%D alternatives to this command. Beware, only the simple one has \type {\noexpand}
168%D before its argument.
169
170\lettonothing\m_syst_helpers_expanded
171
172\pushoverloadmode
173
174\permanent\protected\def\expanded#1%
175  {\xdef\m_syst_helpers_expanded{\noexpand#1}\m_syst_helpers_expanded}
176
177\popoverloadmode
178
179\permanent\protected\def\startexpanded#1\stopexpanded
180  {\xdef\m_syst_helpers_expanded{#1}\m_syst_helpers_expanded}
181
182\permanent\protected\lettonothing\stopexpanded
183
184%D Recent \TEX\ engines have a primitive \type {\expanded} and we will use that when
185%D possible. After all, we can make not expandable macros now. The name clash is an
186%D unfortunate fact and in retrospect I should nto have agreed to a primitive having
187%D that same name.
188
189% We cannot use the next variant as first we need to adapt \type {##}'s in callers:
190%
191% \def\expanded#1%
192%   {\normalexpanded{\noexpand#1}}
193%
194% \def\startexpanded#1\stopexpanded
195%   {\normalexpanded{#1}}
196
197%D \macros
198%D   {gobbleoneargument,gobble...arguments}
199%D
200%D The next set of macros just do nothing, except that they get rid of a number of
201%D arguments. These zero references prevent intermediate storage. In principle that
202%D is more efficient but it probably won't be noticed. It's anyway cheaper on memory
203%D access. We use versions that don't even store the arguments.
204
205%permanent\def\gobbleoneargument               #-{}
206\permanent\def\gobbletwoarguments            #-#-{}
207\permanent\def\gobblethreearguments        #-#-#-{}
208\permanent\def\gobblefourarguments       #-#-#-#-{}
209\permanent\def\gobblefivearguments       #-#-#-#-{}
210\permanent\def\gobblesixarguments        #-#-#-#-{}
211\permanent\def\gobblesevenarguments    #-#-#-#-#-{}
212\permanent\def\gobbleeightarguments  #-#-#-#-#-#-{}
213\permanent\def\gobbleninearguments #-#-#-#-#-#-#-{}
214
215\permanent\tolerant\def\gobbleoneoptional   [#S#-]{}
216\permanent\tolerant\def\gobbletwooptionals  [#S#-]#*[#S#-]{}
217\permanent\tolerant\def\gobblethreeoptionals[#S#-]#*[#S#-]#*[#S#-]{}
218\permanent\tolerant\def\gobblefouroptionals [#S#-]#*[#S#-]#*[#S#-]#*[#S#-]{}
219\permanent\tolerant\def\gobblefiveoptionals [#S#-]#*[#S#-]#*[#S#-]#*[#S#-]#*[#S#-]{}
220
221%D Reserved macros for tests:
222
223%D A \type \type {\let} is more efficient than a \type {\def} so we often let something
224%D to relax or do nothing. However, we then loose the name in tracing. For that we now
225%D have \type {\lettonothing \foo} which is efficient but also keeps the name. Of course
226%D this is no solution for commands that take arguments but at least it helps tracing a
227%D bit. We keep of course \type {\donothing}.
228
229%D Maybe even nicer is a let that aliases but keeps the name.
230
231       % \lettonothing\donothing
232         \immutable\def\donothing        {} % better in tracing
233%untraced\immutable\def\untraceddonothing{}
234
235\lettonothing\m_syst_string_one
236\lettonothing\m_syst_string_two
237\lettonothing\m_syst_string_three
238\lettonothing\m_syst_string_four
239
240\lettonothing\m_syst_action_yes
241\lettonothing\m_syst_action_nop
242
243%D \macros
244%D   {doifnextcharelse}
245%D
246%D This macro has a long history. Among the things we had to deal with was ignoring
247%D blank spaces but in the meantime we can use some \LUATEX\ trickery. Older versions
248%D use more code and can be find in the \MKIV\ and \MKII\ files.
249
250% \mutable\let\next          \relax
251% \mutable\let\nextnext      \relax % kind of obsolete
252% \mutable\let\nextnextnext  \relax % kind of obsolete
253% \mutable\let\nexttoken     \relax
254
255\permanent\protected\def\doifelsenextchar#1#2#3% #1 should not be {} !
256  {\def\m_syst_action_yes{#2}%
257   \def\m_syst_action_nop{#3}%
258   \futureexpandis#1\m_syst_action_yes\m_syst_action_nop}
259
260\permanent\protected\def\doifelsenextcharcs % #1#2#3% #1 should not be {} !
261  {\futureexpandis}
262
263\aliased\let\doifnextcharelse  \doifelsenextchar
264\aliased\let\doifnextcharcselse\doifelsenextcharcs
265
266%D Because we will mostly use this macro for testing if the next character is \type
267%D {[}, we also make a slightly faster variant as it is not uncommon to have tens of
268%D thousands of calls to this test in a run. Of course it also is more convenient to
269%D read a trace then. Here we use a lookahead primitive that ignores (blank) spaces
270%D which makes for less tracing clutter than the original definition. It's also
271%D faster bit that will probably go unnoticed. Of course, hard||core \TEX ies whose
272%D reputations depends on understanding obscure macro definitions will love the more
273%D low level variants.
274
275\permanent\protected\def\doifelsenextoptional#1#2%
276  {\def\m_syst_action_yes{#1}%
277   \def\m_syst_action_nop{#2}%
278   \futureexpandis[\m_syst_action_yes\m_syst_action_nop}
279
280\permanent\protected\def\doifelsenextoptionalcs
281  {\futureexpandis[}
282
283\aliased\let\doifnextoptionalelse  \doifelsenextoptional
284\aliased\let\doifnextoptionalcselse\doifelsenextoptionalcs
285
286\permanent\protected\def\doifelsenextbgroup#1#2%
287  {\def\m_syst_action_yes{#1}%
288   \def\m_syst_action_nop{#2}%
289   \futureexpandis\bgroup\m_syst_action_yes\m_syst_action_nop}
290
291\permanent\protected\def\doifelsenextbgroupcs % #1#2
292  {\futureexpandis\bgroup}
293
294\aliased\let\doifnextbgroupelse  \doifelsenextbgroup
295\aliased\let\doifnextbgroupcselse\doifelsenextbgroupcs
296
297\permanent\protected\def\doifelsenextparenthesis#1#2%
298  {\def\m_syst_action_yes{#1}%
299   \def\m_syst_action_nop{#2}%
300   \futureexpandis(\m_syst_action_yes\m_syst_action_nop}
301
302\aliased\let\doifnextparenthesiselse\doifelsenextparenthesis
303
304%D The next one is handy in predictable situations:
305
306\permanent\protected\def\doifelsefastoptionalcheck#1#2%
307  {\def\m_syst_action_yes{#1}%
308   \def\m_syst_action_nop{#2}%
309   \futureexpandis[\m_syst_action_yes\m_syst_action_nop}
310
311\permanent\protected\def\doifelsefastoptionalcheckcs
312  {\futureexpandis[}
313
314\aliased\let\doiffastoptionalcheckelse  \doifelsefastoptionalcheck
315\aliased\let\doiffastoptionalcheckcselse\doifelsefastoptionalcheckcs
316
317%D Here's one for skipping spaces and pars, handy for:
318%D
319%D \starttyping
320%D \hbox
321%D
322%D   {a few lines later}
323%D \stoptyping
324%D
325%D like:
326%D
327%D \starttyping
328%D \def\somecommand{\dowithnextbox{\box\nextbox}\ruledhbox}
329%D
330%D \assumelongusagecs\somecommand
331%D
332%D \bgroup
333%D     oeps
334%D \egroup
335%D \stoptyping
336%D
337%D The original kind of clumsy but working version is now replaced by a simple
338%D macro. And it can be even less code in \LUAMETATEX:
339
340\aliased\let\assumelongusagecs\expandafterpars % so we can replace it
341
342%D It's used to skip over empty lines in some constructs that we like to set up
343%D spacy. We already permit par tokens (and equivalents) in math and some other
344%D places visa engine features. And, \type {\long} stuff has been dropped for a long
345%D time already (it is still optional in \lUATEX). But the \quote  {long} in the
346%D name kind of stuck.
347
348%D \macros
349%D   {blankspace}
350%D
351%D Here is how this works. The \type {\let} primitive first picks up the to be let
352%D name. Then it scans for an optional equal and when that is found it will skip the
353%D next space, which is why we need an extra one to achieve our goal. Such a \type
354%D {\blankspace} has the meaning \typ {blank space}. A typical \TEX ie definition:
355
356\normalexpanded{\permanent\let\noexpand\blankspace=\space\space}
357
358%D \macros
359%D   {setvalue,setgvalue,setevalue,setxvalue,
360%D    letvalue,letgvalue,getvalue,resetvalue,
361%D    undefinevalue,ignorevalue}
362%D
363%D \TEX's primitive \type {\csname} can be used to construct all kind of commands
364%D that cannot be defined with \type {\def} and \type {\let}. Every macro programmer
365%D sooner or later wants macros like these.
366%D
367%D \starttyping
368%D \setvalue   {name}{...} = \def\name{...}
369%D \setgvalue  {name}{...} = \gdef\name{...}
370%D \setevalue  {name}{...} = \edef\name{...}
371%D \setxvalue  {name}{...} = \xdef\name{...}
372%D \letvalue   {name}=\... = \let\name=\...
373%D \letgvalue  {name}=\... = \glet\name=\...
374%D \getvalue   {name}      = \name
375%D \resetvalue {name}      = \def\name{}
376%D \stoptyping
377%D
378%D As we will see, \CONTEXT\ uses these commands many times, which is mainly due to
379%D its object oriented and parameter driven character.
380
381\permanent\def\setvalue     #1{\defcsname #1\endcsname}
382\permanent\def\setgvalue    #1{\gdefcsname#1\endcsname}
383\permanent\def\setevalue    #1{\edefcsname#1\endcsname}
384\permanent\def\setxvalue    #1{\xdefcsname#1\endcsname}
385\permanent\def\getvalue     #1{\begincsname#1\endcsname}
386\permanent\def\letvalue     #1{\letcsname #1\endcsname}
387\permanent\def\letgvalue    #1{\gletcsname#1\endcsname}
388\permanent\def\resetvalue   #1{\letcsname#1\endcsname\empty}
389\permanent\def\undefinevalue#1{\letcsname#1\endcsname\undefined}
390\permanent\def\ignorevalue#1#2{\letcsname#1\endcsname\empty}
391
392\permanent\def\setuvalue    #1{\protected\defcsname #1\endcsname}
393\permanent\def\setuevalue   #1{\protected\edefcsname#1\endcsname}
394\permanent\def\setugvalue   #1{\protected\gdefcsname#1\endcsname}
395\permanent\def\setuxvalue   #1{\protected\xdefcsname#1\endcsname}
396
397\permanent\protected\def\getuvalue#1{\begincsname#1\endcsname}
398
399%D \macros
400%D   {globallet,glet}
401%D
402%D In \CONTEXT\ of May 2000 using \type {\globallet} instead of the two tokens will
403%D save us some $300\times4=1200$ bytes of format file on a 32~bit system. Not that
404%D it matters much today. But nowadays we can just alias to a primitive:
405
406\aliased\let\globallet\glet
407
408%D \macros
409%D   {doifundefined,doifdefined,
410%D    doifundefinedelse,doifdefinedelse,
411%D    doifalldefinedelse}
412%D
413%D The standard way of testing if a macro is defined is comparing its meaning with
414%D another undefined one, usually \type {\undefined}. To garantee correct working of
415%D the next set of macros, \type {\undefined} may never be defined!
416%D
417%D \starttyping
418%D \doifundefined      {string}    {...}
419%D \doifdefined        {string}    {...}
420%D \doifundefinedelse  {string}    {then ...} {else ...}
421%D \doifdefinedelse    {string}    {then ...} {else ...}
422%D \doifalldefinedelse {commalist} {then ...} {else ...}
423%D \stoptyping
424%D
425%D Every macroname that \TEX\ builds gets an entry in the hash table, which is of
426%D limited size. It is expected that \ETEX\ will offer a less memory||consuming
427%D alternative.
428%D
429%D Although it will probably never be a big problem, it is good to be aware of the
430%D difference between testing on a macro name to be build by using \type {\csname} and
431%D \type {\endcsname} and testing the \type {\name} directly.
432%D
433%D \starttyping
434%D \expandafter\ifrelax\csname NameA\endcsname ... \else ... \fi
435%D
436%D \ifundefined\NameB ... \else ... \fi
437%D \stoptyping
438%D
439%D Suppression of errors while constructing a control sequence is one of the (few)
440%D things that I remember being discussed in the perspective of \ETEX\ but it was
441%D rejected because it was not considered useful. Well, in \LUATEX\ we can surpress
442%D it and that is what we do in \MKIV. We're probably the only macro package that
443%D needs it. That kind of configuration happens elsewhere. These macros are (mostly
444%D for historic reasons) fully expandable.
445
446\permanent\def\doifelseundefined#1%
447  {\ifcsname#1\endcsname
448     \expandafter\secondoftwoarguments\else\expandafter\firstoftwoarguments
449   \fi}
450
451\permanent\def\doifelsedefined#1%
452  {\ifcsname#1\endcsname
453     \expandafter\firstoftwoarguments\else\expandafter\secondoftwoarguments
454   \fi}
455
456\permanent\def\doifundefined#1%
457  {\ifcsname#1\endcsname
458     \expandafter\gobbleoneargument\else\expandafter\firstofoneargument
459   \fi}
460
461\permanent\def\doifdefined#1%
462  {\ifcsname#1\endcsname
463     \expandafter\firstofoneargument\else\expandafter\gobbleoneargument
464   \fi}
465
466\aliased\let\doifundefinedelse\doifelseundefined
467\aliased\let\doifdefinedelse  \doifelsedefined
468
469%D \macros
470%D   {letbeundefined}
471%D
472%D Testing for being undefined comes down to testing on \type {\relax} when we use
473%D \type {\csname}, but when using \type {\ifx}, we test on being \type
474%D {\undefined}! In \ETEX\ we have \type {\ifcsname} and that way of testing on
475%D existance is not the same as the one described here. Therefore we introduce:
476
477\permanent\protected\def\letbeundefined#1%
478  {\letcsname#1\endcsname\undefined}
479
480\permanent\protected\def\localundefine#1% conditional
481  {\ifcsname#1\endcsname\letcsname#1\endcsname\undefined\fi}
482
483\permanent\protected\def\globalundefine#1% conditional
484  {\ifcsname#1\endcsname\gletcsname#1\endcsname\undefined\fi}
485
486%D Beware, being \type {\undefined} in \ETEX\ means that the macro {\em is} defined!
487%D
488%D When we were developing the scientific units module, we encountered different
489%D behavior in text and math mode, which was due to this grouping subtilities. We
490%D therefore decided to use \type {\begingroup} instead of \type {\bgroup}.
491%D
492%D \stoptyping
493%D \doifelsealldefined  {foo,ofo}  {YES}{NOP}
494%D \doifelseallundefined{foo,ofo}  {YES}{NOP}
495%D \doifelsealldefined  {relax,ofo}{YES}{NOP}
496%D \doifelseallundefined{foo,relax}{YES}{NOP}
497%D \stoptyping
498
499\def\syst_helpers_do_if_all_defined_else#1%
500  {\ifcsname#1\endcsname\else
501     \donefalse
502     \expandafter\quitcommalist % added
503   \fi}
504
505\def\syst_helpers_do_if_all_undefined_else#1%
506  {\ifcsname#1\endcsname
507     \donefalse
508     \expandafter\quitcommalist
509   \fi}
510
511\permanent\protected\def\syst_helpers_do_if_all_else#1#2%
512  {\begingroup
513   \donetrue % we could use a reserved one and avoid the group
514   \processcommalist[#2]#1%
515   \ifdone
516     \endgroup\expandafter\firstoftwoarguments
517   \else
518     \endgroup\expandafter\secondoftwoarguments
519   \fi}
520
521\permanent\protected\def\doifelsealldefined  {\syst_helpers_do_if_all_else\syst_helpers_do_if_all_defined_else}
522\permanent\protected\def\doifelseallundefined{\syst_helpers_do_if_all_else\syst_helpers_do_if_all_undefined_else}
523
524\aliased\let\doifalldefinedelse  \doifelsealldefined
525\aliased\let\doifallundefinedelse\doifelseallundefined
526
527%D \macros
528%D   {doif,doifelse,doifnot}
529%D
530%D Programming in \TEX\ differs from programming in procedural languages like
531%D \MODULA. This means that one --- well, let me speek for myself --- tries to do
532%D the things in the well known way. Therefore the next set of \type {ifthenelse}
533%D commands were between the first ones we needed. A few years later, the opposite
534%D became true: when programming in \MODULA, I sometimes miss handy things like
535%D grouping, runtime redefinition, expansion etc. While \MODULA\ taught me to
536%D structure, \TEX\ taught me to think recursive.
537%D
538%D \starttyping
539%D \doif     {string1} {string2} {...}
540%D \doifnot  {string1} {string2} {...}
541%D \doifelse {string1} {string2} {then ...}{else ...}
542%D \stoptyping
543%D
544%D Again, we use some of the new primitives in \LUAMETATEX. Using straightforward
545%D \type {\edef}'s and \type {\ifx} comparison works as well, but this saves tokens
546%D and, more important, tracing clutter.
547
548\permanent\protected\def\doifelse#1#2%
549  {\iftok{#1}{#2}%
550     \expandafter\firstoftwoarguments
551   \else
552     \expandafter\secondoftwoarguments
553   \fi}
554
555\permanent\protected\def\doif#1#2%
556  {\iftok{#1}{#2}%
557     \expandafter\firstofoneargument
558   \else
559     \expandafter\gobbleoneargument
560   \fi}
561
562\permanent\protected\def\doifnot#1#2%
563  {\iftok{#1}{#2}%
564     \expandafter\gobbleoneargument
565   \else
566     \expandafter\firstofoneargument
567   \fi}
568
569%D \macros
570%D   {doifempty,doifemptyelse,doifnotempty}
571%D
572%D We complete our set of conditionals with:
573%D
574%D \starttyping
575%D \doifempty     {string} {...}
576%D \doifnotempty  {string} {...}
577%D \doifemptyelse {string} {then ...} {else ...}
578%D \stoptyping
579%D
580%D This time, the string is not expanded, but we use the dedicated empty checker
581%D here.
582
583\permanent\protected\def\doifelseempty#1%
584  {\def\m_syst_string_one{#1}%
585   \ifempty\m_syst_string_one
586     \expandafter\firstoftwoarguments
587   \else
588     \expandafter\secondoftwoarguments
589   \fi}
590
591% or (test this):
592%
593% \permanent\protected\def\processallactionsinset[#1]%
594%   {\ifempty{#1}%
595%      \expandafter\processaction
596%    \else
597%      \expandafter\syst_helpers_process_all_actions_in_set_indeed
598%    \fi[#1]}
599
600\aliased\let\doifemptyelse\doifelseempty
601
602\permanent\protected\def\doifempty#1%
603  {\def\m_syst_string_one{#1}%
604   \ifempty\m_syst_string_one
605     \expandafter\firstofoneargument
606   \else
607     \expandafter\gobbleoneargument
608   \fi}
609
610\permanent\protected\def\doifnotempty#1%
611  {\def\m_syst_string_one{#1}%
612   \ifempty\m_syst_string_one
613     \expandafter\gobbleoneargument
614   \else
615     \expandafter\firstofoneargument
616   \fi}
617
618%D \macros
619%D   {doifinset,doifnotinset,doifinsetelse}
620%D
621%D We can check if a string is present in a comma separated set of strings.
622%D Depending on the result, some action is taken.
623%D
624%D \starttyping
625%D \doifinset     {string} {string,...} {...}
626%D \doifnotinset  {string} {string,...} {...}
627%D \doifinsetelse {string} {string,...} {then ...} {else ...}
628%D \stoptyping
629
630% !0nop=\doifinsetelse{ccc}{,}{yes}{nop}
631% !0nop=\doifinsetelse{ccc}{,,}{yes}{nop}
632% !0nop=\doifinsetelse{ccc}{,,,}{yes}{nop}
633
634% !1nop=\doifinsetelse{}{}{yes}{nop}
635% !2yes=\doifinsetelse{aaa}{bbb,ccc,ddd,aaa,eee}{yes}{nop}
636% !3nop=\doifinsetelse{aaa}{bbb}{yes}{nop}
637% !4yes=\doifinsetelse{aaa}{aaa}{yes}{nop}
638% !5nop=\doifinsetelse{aaaa}{bbb,ccc,ddd,aaa,eee}{yes}{nop}
639% !6nop=\doifinsetelse{}{}{yes}{nop}
640% !7nop=\doifinsetelse{}{aaa}{yes}{nop}
641% !8nop=\doifinsetelse{aaa}{}{yes}{nop}
642
643% !1=\doifinset{}{}{yes}
644% !2yes=\doifinset{aaa}{bbb,ccc,ddd,aaa,eee}{yes}
645% !3=\doifinset{aaa}{bbb}{yes}
646% !4yes=\doifinset{aaa}{aaa}{yes}
647% !5=\doifinset{}{}{yes}
648% !6=\doifinset{aaa}{}{yes}
649
650% !1yes=\doifnotinset{}{}{yes}
651% !2=\doifnotinset{aaa}{bbb,ccc,ddd,aaa,eee}{yes}
652% !3yes=\doifnotinset{aaa}{bbb}{yes}
653% !4=\doifnotinset{aaa}{aaa}{yes}
654% !5yes=\doifnotinset{}{}{yes}
655% !6yes=\doifnotinset{aaa}{}{yes}
656
657%D These are normally only used for keywords, i.e.\ strings so we can delegate
658%D the work to \LUA:
659
660%protected\def\doifelseinset#1#2{\clf_doifelseinset{#1}{#2}}
661%protected\def\doifinset    #1#2{\clf_doifinset    {#1}{#2}}
662%protected\def\doifnotinset #1#2{\clf_doifnotinset {#1}{#2}}
663%       % \let\firstinset        \clf_firstinset
664
665% These don't accept spaces after commas:
666%
667% \protected\def\doifelseinset#1#2%
668%   {\ifhasxtoks{,#1,}{,#2,}%
669%      \expandafter\firstoftwoarguments
670%    \else
671%      \expandafter\secondoftwoarguments
672%    \fi}
673
674% \protected\def\doifinset#1#2%
675%   {\ifhasxtoks{,#1,}{,#2,}%
676%      \expandafter\firstofoneargument
677%    \else
678%      \expandafter\gobbleoneargument
679%    \fi}
680
681% \protected\def\doifnotinset#1#2%
682%   {\ifhasxtoks{,#1,}{,#2,}%
683%      \expandafter\gobbleoneargument
684%    \else
685%      \expandafter\firstofoneargument
686%    \fi}
687
688% But these do:
689
690\immutable\edef\a!comma{\expandtoken \ignorecatcode \commaasciicode}
691\immutable\edef\a!space{\expandtoken \ignorecatcode \spaceasciicode}
692
693\normalexpanded {
694
695    \permanent \protected \def \noexpand \doifelseinset#1#2%
696      {\noexpand\ifhasxtoks{,\a!space#1,}{,#2,}%
697         \noexpand\expandafter\noexpand\firstoftwoarguments
698       \noexpand\else
699         \noexpand\expandafter\noexpand\secondoftwoarguments
700       \noexpand\fi}
701
702    \permanent \protected \def \noexpand \doifinset#1#2%
703      {\noexpand\ifhasxtoks{,\a!space#1,}{,#2,}%
704         \noexpand\expandafter\noexpand\firstofoneargument
705       \noexpand\else
706         \noexpand\expandafter\noexpand\gobbleoneargument
707       \noexpand\fi}
708
709    \permanent \protected \def \noexpand \doifnotinset#1#2%
710      {\noexpand\ifhasxtoks{,\a!space#1,}{,#2,}%
711         \noexpand\expandafter\noexpand\gobbleoneargument
712       \noexpand\else
713         \noexpand\expandafter\noexpand\firstofoneargument
714       \noexpand\fi}
715
716}
717
718%D Done.
719
720\aliased\let\doifinsetelse\doifelseinset
721
722%D \macros
723%D   {doifcommon,doifnotcommon,doifcommonelse}
724%D
725%D Probably the most time consuming tests are those that test for overlap in sets
726%D of strings.
727%D
728%D \starttyping
729%D \doifcommon     {string,...} {string,...} {...}
730%D \doifnotcommon  {string,...} {string,...} {...}
731%D \doifcommonelse {string,...} {string,...} {then ...} {else ...}
732%D \stoptyping
733
734% !1yes=\doifcommonelse{aaa,bbb,ccc}{aaa,bbb,ccc}{yes}{nop}
735% !2nop=\doifcommonelse{aaa,bbb,ccc}{ddd,eee,fff}{yes}{nop}
736% !3nop=\doifcommonelse{aaa}{ddd,eee,fff}{yes}{nop}
737% !4yes=\doifcommonelse{aaa}{aaa}{yes}{nop}
738% !5nop=\doifcommonelse{bbb}{aaa}{yes}{nop}
739% !6nop=\doifcommonelse{}{aaa,bbb,ccc}{yes}{nop}
740% !7nop=\doifcommonelse{aaa,bbb,ccc}{}{yes}{nop}
741% !8nop=\doifcommonelse{}{}{yes}{nop}
742
743% !9nop=\doifcommonelse{,,}{,,}{yes}{nop}
744% !9yes=\doifcommonelse{,a,}{,a,}{yes}{nop}
745% !9yes=\doifcommonelse{,,a,}{,a,}{yes}{nop}
746% !9yes=\doifcommonelse{,a,}{,,a,}{yes}{nop}
747% !9yes=\doifcommonelse{,a,}{,,,a,}{yes}{nop}
748% !9yes=\doifcommonelse{,,a,}{,,,a,}{yes}{nop}
749
750% \permanent\protected\def\doifelsecommon#1#2{\clf_doifelsecommon{#1}{#2}} % todo: define in lua
751% \permanent\protected\def\doifcommon    #1#2{\clf_doifcommon    {#1}{#2}} % todo: define in lua
752% \permanent\protected\def\doifnotcommon #1#2{\clf_doifnotcommon {#1}{#2}} % todo: define in lua
753
754\permanent\protected\def\doifelsecommon#1#2{\clf_if_common{#1}{#2}\expandafter\firstoftwoarguments\else\expandafter\secondoftwoarguments\fi}
755\permanent\protected\def\doifcommon    #1#2{\clf_if_common{#1}{#2}\expandafter\firstofoneargument \else\expandafter\gobbleoneargument   \fi}
756\permanent\protected\def\doifnotcommon #1#2{\clf_if_common{#1}{#2}\expandafter\gobbleoneargument  \else\expandafter\firstofoneargument  \fi}
757
758\aliased\let\doifcommonelse\doifelsecommon
759
760%D \macros
761%D   {processcommalist,processcommacommand,quitcommalist,
762%D    processcommalistwithparameters}
763%D
764%D We've already seen some macros that take care of comma separated lists. Such
765%D list can be processed with
766%D
767%D \starttyping
768%D \processcommalist[string,string,...]\commando
769%D \stoptyping
770%D
771%D The user supplied command \type{\commando} receives one argument: the string.
772%D This command permits nesting and spaces after commas are skipped. Empty sets
773%D are no problem.
774%D
775%D \startbuffer
776%D \def\dosomething#1{(#1)}
777%D
778%D 1: \processcommalist [\hbox{$a,b,c,d,e,f$}] \dosomething \par
779%D 2: \processcommalist [{a,b,c,d,e,f}]        \dosomething \par
780%D 3: \processcommalist [{a,b,c},d,e,f]        \dosomething \par
781%D 4: \processcommalist [a,b,{c,d,e},f]        \dosomething \par
782%D 5: \processcommalist [a{b,c},d,e,f]         \dosomething \par
783%D 6: \processcommalist [{a,b}c,d,e,f]         \dosomething \par
784%D 7: \processcommalist []                     \dosomething \par
785%D 8: \processcommalist [{[}]                  \dosomething \par
786%D \stopbuffer
787%D
788%D \typebuffer
789%D
790%D Or expanded:
791%D
792%D \blank \getbuffer \blank
793%D
794%D The original definitions can be found elsewhere and need a bit more code. In case of
795%D issues, consult the \MKIV\ variant or previous \LMTX\ variants in the repository.
796%D
797%D When a list is saved in a macro, we can use a construction like:
798%D
799%D \starttyping
800%D \expandafter\processcommalist\expandafter[\list]\command
801%D \stoptyping
802%D
803%D Such solutions suit most situations, but we wanted a bit more.
804%D
805%D \starttyping
806%D \processcommacommand[string,\stringset,string]\commando
807%D \stoptyping
808%D
809%D where \type{\stringset} is a predefined set, like:
810%D
811%D \starttyping
812%D \def\first{aap,noot,mies}
813%D \def\second{laatste}
814%D
815%D \processcommacommand[\first]\message
816%D \processcommacommand[\first,second,third]\message
817%D \processcommacommand[\first,between,\second]\message
818%D \stoptyping
819%D
820%D Commands that are part of the list are expanded, so the use of this macro has its
821%D limits. We use a new \LUAMETATEX\ feature that intercepts invalid macro arguments
822%D by quitting when \type {\ignorearguments} is seen. That's why we act on the
823%D arguments state. Again it permits leaner and meaner macro definitions with a bit
824%D less clutter in tracing.
825
826\mutable\lettonothing\commalistcommand
827
828% \protected\def\syst_helpers_process_comma_item#+,%
829%   {\ifarguments
830%      \expandafter\syst_helpers_process_comma_item_gobble
831%    \orelse\ifparameter#1\or
832%      \commalistcommand{#1}%
833%      \expandafter\syst_helpers_process_comma_item_next
834%    \else
835%      \expandafter\syst_helpers_process_comma_item_gobble
836%    \fi}
837
838% \def\syst_helpers_process_comma_item_next
839%   {\expandafterspaces\syst_helpers_process_comma_item}
840
841% \protected\def\processcommalist[#1]#2%
842%   {\pushmacro\commalistcommand
843%    \def\commalistcommand{#2}%
844%    \expandafterspaces\syst_helpers_process_comma_item#1,\ignorearguments\ignorearguments\ignorearguments
845%    \popmacro\commalistcommand}
846
847% \protected\def\processcommacommand[#1]#2%
848%   {\pushmacro\commalistcommand
849%    \def\commalistcommand{#2}%
850%    \normalexpanded{\noexpand\expandafterspaces\syst_helpers_process_comma_item#1,}\ignorearguments\ignorearguments\ignorearguments
851%    \popmacro\commalistcommand}
852
853% \permanent\protected\def\processcommalist[#*#+]#2%
854%   {\pushmacro\commalistcommand
855%    \def\commalistcommand{#2}%
856%    \expandafterspaces\syst_helpers_process_comma_item#1,\ignorearguments\ignorearguments\ignorearguments
857%    \popmacro\commalistcommand}
858%
859% \permanent\protected\def\processcommacommand[#*#+]#2%
860%   {\pushmacro\commalistcommand
861%    \def\commalistcommand{#2}%
862%    \normalexpanded{\noexpand\expandafterspaces\syst_helpers_process_comma_item#1,}\ignorearguments\ignorearguments\ignorearguments
863%    \popmacro\commalistcommand}
864
865\tolerant\protected\def\syst_helpers_process_comma_item#*#1,%
866  {\ifarguments\or
867     \commalistcommand{#1}%
868     \expandafter\syst_helpers_process_comma_item_next
869   \fi}
870
871\def\syst_helpers_process_comma_item_next
872  {\expandafterspaces\syst_helpers_process_comma_item}
873
874\permanent\protected\def\processcommalist[#1]#2%
875  {\pushmacro\commalistcommand
876   \def\commalistcommand{#2}%
877   \syst_helpers_process_comma_item#1\ignorearguments\ignorearguments\ignorearguments
878   \popmacro\commalistcommand}
879
880\permanent\protected\def\processcommacommand[#1]#2%
881  {\pushmacro\commalistcommand
882   \def\commalistcommand{#2}%
883   \normalexpanded{\syst_helpers_process_comma_item#1}\ignorearguments\ignorearguments\ignorearguments
884   \popmacro\commalistcommand}
885
886% \let\syst_helpers_process_comma_item_next_a                                                            \syst_helpers_process_comma_item_next
887% \def\syst_helpers_process_comma_item_next_b#-\ignorearguments{\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_a}
888% \def\syst_helpers_process_comma_item_next_c#-\ignorearguments{\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_b}
889% \def\syst_helpers_process_comma_item_gobble#-\ignorearguments{}
890
891\let\syst_helpers_process_comma_item_next_a                                                            \syst_helpers_process_comma_item_next
892\def\syst_helpers_process_comma_item_next_b#-\ignorearguments{\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_a}
893\def\syst_helpers_process_comma_item_next_c#-\ignorearguments{\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_b}
894\def\syst_helpers_process_comma_item_gobble#-\ignorearguments{}
895
896\permanent\protected\def\quitcommalist    {\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_b}
897\permanent\protected\def\quitprevcommalist{\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_c}
898
899%D \startbuffer
900%D \def\foo#1{(#1)}
901%D <\processcommalist[a,b,c,d]\foo>
902%D
903%D \def\foo#1{(#1)}
904%D \def\oof#1{<\processcommalist[#1]\foo>}
905%D <\processcommalist[{a,b},{c,d}]\oof>
906%D
907%D \def\foo#1{(#1)\quitcommalist}
908%D <\processcommalist[a,b,c,d]\foo>
909%D
910%D \def\foo#1{(#1)}
911%D \def\oof#1{<\processcommalist[#1]\foo\quitcommalist>}
912%D <\processcommalist[{a,b},{c,d}]\oof>
913%D
914%D \def\foo#1{(#1)\quitcommalist}
915%D \def\oof#1{<\processcommalist[#1]\foo>}
916%D <\processcommalist[{a,b},{c,d},{e,f}]\oof>
917%D
918%D \def\foo#1{(#1)\quitprevcommalist}
919%D \def\oof#1{<\processcommalist[#1]\foo>}
920%D <\processcommalist[{a,b},{c,d},{e,f}]\oof>
921%D \stopbuffer
922%D
923%D \typebuffer \blank \getbuffer \blank
924
925%D The argument to \type{\command} is not delimited. Because we often use \type {[]}
926%D as delimiters, we also have:
927%D
928%D \starttyping
929%D \processcommalistwithparameters[string,string,...]\command
930%D \stoptyping
931%D
932%D where \type{\command} looks like:
933%D
934%D \starttyping
935%D \def\command[#1]{... #1 ...}
936%D \stoptyping
937
938\let\syst_helpers_do_process_comma_list_with_parameters\gobbleoneargument
939
940\permanent\protected\def\processcommalistwithparameters[#1]#2%
941  {\def\syst_helpers_do_process_comma_list_with_parameters##1{#2[##1]}%
942   \processcommalist[#1]\syst_helpers_do_process_comma_list_with_parameters}
943
944%D \macros
945%D   {startprocesscommalist,startprocesscommacommand}
946%D
947%D Two more:
948
949\let\syst_helpers_comma_list_step\relax
950
951\mutable\lettonothing\currentcommalistitem
952
953\permanent\protected\def\startprocesscommalist[#1]#2\stopprocesscommalist
954  {\def\syst_helpers_comma_list_step##1{\def\currentcommalistitem{##1}#2}%
955   \processcommalist[#1]\syst_helpers_comma_list_step}
956
957\permanent\protected\def\startprocesscommacommand[#1]#2\stopprocesscommacommand
958  {\def\syst_helpers_comma_list_step##1{\def\currentcommalistitem{##1}#2}%
959   \normalexpanded{\processcommalist[#1]}\syst_helpers_comma_list_step}
960
961\permanent\protected\lettonothing\stopprocesscommalist
962\permanent\protected\lettonothing\stopprocesscommacommand
963
964%D \macros
965%D   {processaction,
966%D    processfirstactioninset,
967%D    processallactionsinset}
968%D
969%D \CONTEXT\ makes extensive use of a sort of case or switch command. Depending of
970%D the presence of one or more provided items, some actions is taken. These macros
971%D can be nested without problems.
972%D
973%D \starttyping
974%D \processaction           [x]     [x=>a,y=>b,z=>c]
975%D \processfirstactioninset [x,y,z] [x=>a,y=>b,z=>c]
976%D \processallactionsinset  [x,y,z] [x=>a,y=>b,z=>c]
977%D \stoptyping
978%D
979%D We can supply both a \type {default} action and an action to be undertaken when
980%D an \type {unknown} value is met:
981%D
982%D \starttyping
983%D \processallactionsinset
984%D   [x,y,z]
985%D   [      a=>\a,
986%D          b=>\b,
987%D          c=>\c,
988%D    default=>\default,
989%D    unknown=>\unknown{... \commalistelement ...}]
990%D \stoptyping
991%D
992%D When \type {#1} is empty, this macro scans list \type {#2} for the keyword \type
993%D {default} and executed the related action if present. When \type {#1} is non
994%D empty and not in the list, the action related to \type {unknown} is executed.
995%D Both keywords must be at the end of list \type{#2}. Afterwards, the actually
996%D found keyword is available in \type {\commalistelement}. An advanced example of
997%D the use of this macro can be found in \PPCHTEX, where we completely rely on \TEX\
998%D for interpreting user supplied keywords like \type {SB}, \type {SB1..6}, \type
999%D {SB125} etc.
1000%D
1001%D In the meantime we follow a different approach, often somewhat more heavy on the
1002%D number of control sequences used, but that is no lomger a real issue. The code
1003%D has been simplified and nwo uses the macro stack mechanism. If needed I can make
1004%D this more hip and a bit faster now but \unknown\ it's seldom used nowadays as we
1005%D have better ways now.
1006
1007\mutable\lettonothing\commalistelement
1008
1009\lettonothing\m_syst_string_one
1010\lettonothing\m_syst_string_two
1011
1012\let\syst_helpers_do_compare_process_action\relax
1013
1014\protected\def\syst_helpers_do_compare_process_action_a[#1=>#2][#3]%
1015  {\edef\m_syst_string_two{#1}%
1016   \ifx\m_syst_string_two\s!default
1017     \lettonothing\commalistelement
1018     #2%
1019   \fi}
1020
1021\protected\def\syst_helpers_do_compare_process_action_b[#1=>#2][#3]%
1022  {\edef\m_syst_string_two{#1}%
1023   \ifx\m_syst_string_one\m_syst_string_two
1024     \def\commalistelement{#3}%
1025     #2%
1026     \expandafter\quitcommalist
1027   \orelse\ifx\m_syst_string_two\s!unknown
1028     \def\commalistelement{#3}% beware of loops
1029     #2%
1030   \fi}
1031
1032\permanent\protected\def\processaction[#1]#2[%
1033  {\edef\m_syst_string_one{#1}%
1034   \ifempty\m_syst_string_one
1035     \let\syst_helpers_do_compare_process_action\syst_helpers_do_compare_process_action_a
1036   \else
1037     \let\syst_helpers_do_compare_process_action\syst_helpers_do_compare_process_action_b
1038   \fi
1039   \edef\syst_helpers_do_process_action##1{\syst_helpers_do_compare_process_action[##1][#1]}% expands #1
1040   \processnextcommalist\syst_helpers_do_process_action[}
1041
1042\protected\def\syst_helpers_do_compare_process_action_c[#1=>#2][#3]%
1043  {\edef\m_syst_string_one{#1}%
1044   \edef\m_syst_string_two{#3}%
1045   \ifx\m_syst_string_one\m_syst_string_two
1046     \def\commalistelement{#3}%
1047     #2%
1048     \expandafter\quitprevcommalist
1049   \else
1050     \edef\m_syst_string_one{#1}%
1051     \ifx\m_syst_string_one\s!unknown
1052       \def\commalistelement{#3}%
1053       #2%
1054     \fi
1055   \fi}
1056
1057\permanent\protected\def\processfirstactioninset[#1]%
1058  {\edef\m_syst_string_one{#1}%
1059   \ifempty\m_syst_string_one
1060     \expandafter\processaction
1061   \else
1062     \expandafter\syst_helpers_process_first_action_in_set_indeed
1063   \fi
1064   [#1]}
1065
1066\let\syst_helpers_do_process_action   \gobbleoneargument
1067\let\syst_helpers_do_do_process_action\gobbleoneargument
1068
1069\tolerant\protected\def\syst_helpers_process_first_action_in_set_indeed[#1]#*[#2]%
1070  {\def\syst_helpers_do_process_action##1%
1071     {\def\syst_helpers_do_do_process_action####1{\syst_helpers_do_compare_process_action_c[####1][##1]}%
1072      \processcommalist[#2]\syst_helpers_do_do_process_action}%
1073   \normalexpanded{\processcommalist[#1]}\syst_helpers_do_process_action}
1074
1075\protected\def\syst_helpers_do_compare_process_action_d[#1=>#2][#3]%
1076  {\edef\m_syst_string_one{#1}%
1077   \edef\m_syst_string_two{#3}%
1078   \ifx\m_syst_string_one\m_syst_string_two
1079     \def\commalistelement{#3}%
1080     #2%
1081     \expandafter\quitcommalist
1082   \else
1083     \edef\m_syst_string_one{#1}%
1084     \ifx\m_syst_string_one\s!unknown
1085       \def\commalistelement{#3}%
1086       #2%
1087     \fi
1088   \fi}
1089
1090\let\syst_process_action_in_set_all\relax
1091
1092\tolerant\protected\def\syst_helpers_process_all_actions_in_set_indeed[#1]#*[#2]%
1093  {\globalpushmacro\syst_process_action_in_set_all
1094   \def\syst_process_action_in_set##1%
1095     {\def\syst_process_action_in_set_one####1{\syst_helpers_do_compare_process_action_d[####1][##1]}%
1096      \processcommalist[#2]\syst_process_action_in_set_one}%
1097      \processcommacommand[#1]\syst_process_action_in_set
1098   \globalpopmacro\syst_process_action_in_set_all}
1099
1100\permanent\protected\def\processallactionsinset[#1]%
1101  {\edef\m_syst_string_one{#1}%
1102   \ifempty\m_syst_string_one
1103     \expandafter\processaction
1104   \else
1105     \expandafter\syst_helpers_process_all_actions_in_set_indeed
1106   \fi[#1]}
1107
1108%D These macros use:
1109
1110% \protected\def\processnextcommalist#1[#2#3]%
1111%   {\pushmacro\commalistcommand
1112%    \def\commalistcommand{#1}%
1113%    \expandafterspaces\syst_helpers_process_comma_item#2#3\ignorearguments\ignorearguments\ignorearguments
1114%    \popmacro\commalistcommand}
1115
1116\permanent\protected\def\processnextcommalist#1[#2#3]% watch out: mkiv has two extra initial arguments
1117  {\pushmacro\commalistcommand
1118   \def\commalistcommand{#1}%
1119   \expandafterspaces\syst_helpers_process_comma_item#2#3\ignorearguments\ignorearguments\ignorearguments
1120   \popmacro\commalistcommand}
1121
1122%D \macros
1123%D   {getfirstcharacter, firstcharacter, remainingcharacters, doiffirstcharacter}
1124%D
1125%D Sometimes the action to be undertaken depends on the next character. This macro
1126%D get this character and puts it in \type {\firstcharacter}.
1127%D
1128%D \starttyping
1129%D \getfirstcharacter {string}
1130%D \stoptyping
1131%D
1132%D A two step expansion is used to prevent problems with complicated arguments, for
1133%D instance arguments that consist of two or more expandable tokens.
1134
1135\mutable\lettonothing\firstcharacter
1136\mutable\lettonothing\remainingcharacters
1137
1138\permanent\protected\def\getfirstcharacter     #1{\clf_getfirstcharacter{#1}}
1139\permanent\protected\def\doifelsefirstchar   #1#2{\clf_doifelsefirstchar{#1}{#2}}
1140\permanent\protected\def\thefirstcharacter     #1{\clf_thefirstcharacter{#1}}
1141\permanent\protected\def\theremainingcharacters#1{\clf_theremainingcharacters{#1}}
1142
1143\aliased\let\doiffirstcharelse\doifelsefirstchar
1144
1145%D \macros
1146%D   {doifinstringelse, doifincsnameelse}
1147%D
1148%D We can check for the presence of a substring in a given sequence of characters.
1149%D
1150%D \starttyping
1151%D \doifinstringelse {substring} {string} {then ...} {else ...}
1152%D \stoptyping
1153%D
1154%D \startbuffer
1155%D \doifinstringelse{abc}{foo bar abc}{Y}{N}=Y\par
1156%D \doifinstringelse{abc}{foo bar cab}{Y}{N}=N\par
1157%D \doifinstring    {abc}{foo bar abc}{Y}=Y\par
1158%D \doifinstring    {abc}{foo bar cab}{Y}\par
1159%D \doifnotinstring {abc}{foo bar abc}{Y}\par
1160%D \doifnotinstring {abc}{foo bar cab}{Y}=Y\par
1161%D \doifinstringelse{}{foo bar abc}{Y}{N}=N\par
1162%D \doifinstring    {}{foo bar abc}{N}\par
1163%D \doifnotinstring {}{foo bar abc}{Y}=Y\par
1164%D \doifinstringelse{x}{}{Y}{N}=N\par
1165%D \doifinstring    {x}{}{N}\par
1166%D \doifnotinstring {x}{}{Y}=Y\par
1167%D \doifinstringelse{}{}{Y}{N}=N\par
1168%D \doifinstring    {}{}{N}\par
1169%D \doifnotinstring {}{}{Y}=Y\par
1170%D \stopbuffer
1171%D
1172%D \typebuffer \blank \getbuffer \blank
1173
1174%D I keep the following as example code:
1175
1176% \let\syst_helpers_do_do_if_in_string_else\relax
1177% \let\syst_helpers_do_do_if_in_string     \relax
1178% \let\syst_helpers_do_do_if_not_in_string \relax
1179%
1180% \lettonothing\m_syst_sub_string
1181%
1182% \protected\def\doifelseinstring#1%
1183%   {\edef\m_syst_sub_string{#1}% expand #1 here
1184%    \ifempty\m_syst_sub_string
1185%      \expandafter\thirdofthreearguments
1186%    \else
1187%      \expandafter\syst_helpers_do_if_in_string_else
1188%    \fi}
1189%
1190% \let\doifinstringelse\doifelseinstring
1191%
1192% \protected\def\syst_helpers_do_if_in_string_else#1% ##2 can be {abc}
1193%   {\normalexpanded{\protected\def\syst_helpers_do_do_if_in_string_else##1\m_syst_sub_string##2}%
1194%      {\ifarguments
1195%       \or
1196%         \expandafter\syst_helpers_do_if_in_string_else_nop
1197%       \or
1198%         \expandafter\syst_helpers_do_if_in_string_else_yes
1199%       \fi}%
1200%    \normalexpanded{\syst_helpers_do_do_if_in_string_else#1}\s!e_o_t_token\ignorearguments\ignorearguments}
1201%
1202% \protected\def\syst_helpers_do_if_in_string_else_delimited#1% ##2 can be {abc}
1203%   {\normalexpanded{\protected\def\syst_helpers_do_do_if_in_string_else##1,\m_syst_sub_string,##2}%
1204%      {\ifarguments
1205%       \or
1206%         \expandafter\syst_helpers_do_if_in_string_else_nop
1207%       \or
1208%         \expandafter\syst_helpers_do_if_in_string_else_yes
1209%       \fi}%
1210%    \normalexpanded{\syst_helpers_do_do_if_in_string_else,#1,}\s!e_o_t_token\ignorearguments\ignorearguments}
1211%
1212% \protected\def\doifinstring#1%
1213%   {\edef\m_syst_sub_string{#1}% expand #1 here
1214%    \ifempty\m_syst_sub_string
1215%      \expandafter\gobbletwoarguments
1216%    \else
1217%      \expandafter\syst_helpers_do_if_in_string
1218%    \fi}
1219%
1220% \protected\def\syst_helpers_do_if_in_string#1% ##2 can be {abc}
1221%   {\normalexpanded{\protected\def\syst_helpers_do_do_if_in_string##1\m_syst_sub_string##2}%
1222%      {\ifarguments
1223%       \or
1224%         \expandafter\syst_helpers_do_if_in_string_nop
1225%       \or
1226%         \expandafter\syst_helpers_do_if_in_string_yes
1227%       \fi}%
1228%    \normalexpanded{\syst_helpers_do_do_if_in_string#1}\s!e_o_t_token\ignorearguments\ignorearguments}
1229%
1230% \protected\def\doifnotinstring#1%
1231%   {\edef\m_syst_sub_string{#1}% expand #1 here
1232%    \ifempty\m_syst_sub_string
1233%      \expandafter\secondoftwoarguments
1234%    \else
1235%      \expandafter\syst_helpers_do_if_not_in_string
1236%    \fi}
1237%
1238% \protected\def\syst_helpers_do_if_not_in_string#1% ##2 can be {abc}
1239%   {\normalexpanded{\protected\def\syst_helpers_do_do_if_not_in_string##1\m_syst_sub_string##2}%
1240%      {\ifarguments
1241%       \or
1242%         \expandafter\syst_helpers_do_if_not_in_string_nop
1243%       \or
1244%         \expandafter\syst_helpers_do_if_not_in_string_yes
1245%       \fi}%
1246%    \normalexpanded{\syst_helpers_do_do_if_not_in_string#1}\s!e_o_t_token\ignorearguments\ignorearguments}
1247%
1248% \def\syst_helpers_do_if_in_string_else_yes#0\ignorearguments\ignorearguments#2#0{#2} % of #- and #1
1249% \def\syst_helpers_do_if_in_string_else_nop#0\ignorearguments#0#3{#3}
1250% \def\syst_helpers_do_if_in_string_yes     #0\ignorearguments\ignorearguments#2{#2}
1251% \def\syst_helpers_do_if_in_string_nop     #0\ignorearguments#0{}
1252% \def\syst_helpers_do_if_not_in_string_yes #0\ignorearguments\ignorearguments#0{}
1253% \def\syst_helpers_do_if_not_in_string_nop #0\ignorearguments#2{#2}
1254
1255\permanent\protected\def\doifelseinstring#1#2%
1256  {\ifhasxtoks{#1}{#2}%
1257     \expandafter\firstoftwoarguments
1258   \else
1259     \expandafter\secondoftwoarguments
1260   \fi}
1261
1262\permanent\protected\def\doifinstring#1#2%
1263  {\ifhasxtoks{#1}{#2}%
1264     \expandafter\firstofoneargument
1265   \else
1266     \expandafter\gobbleoneargument
1267   \fi}
1268
1269\permanent\protected\def\doifnotinstring#1#2%
1270  {\ifhasxtoks{#1}{#2}%
1271     \expandafter\gobbleoneargument
1272   \else
1273     \expandafter\firstofoneargument
1274   \fi}
1275
1276\aliased\let\doifinstringelse\doifelseinstring
1277
1278%D The next one one of those variants that we used when speed was more of an issue
1279%D that today. Now we just expand the lot. We just use an alias now:
1280
1281\aliased\let\doifelseincsname\doifelseinstring
1282\aliased\let\doifincsnameelse\doifinstringelse
1283
1284%D \macros
1285%D   {doifnumberelse,doifnumber,doifnotnumber}
1286%D
1287%D The next macro executes a command depending of the outcome of a test on numerals.
1288%D We now use a \LUATEX\ feature that permits a more robust checking, but you might
1289%D want to take a look at the originals. It's typically one of these new features
1290%D that probably only \CONTEXT\ will use, which is probably true for more such
1291%D features that no one ever asked for (but they are pretty generic in nature
1292%D anyway).
1293
1294\permanent\def\doifelsenumber#1%
1295  {\ifchknum#1\or
1296     \expandafter\firstoftwoarguments
1297   \else
1298     \expandafter\secondoftwoarguments
1299   \fi}
1300
1301\permanent\def\doifnumber#1%
1302  {\ifchknum#1\or
1303     \expandafter\firstoftwoarguments
1304   \else
1305     \expandafter\gobbleoneargument
1306   \fi}
1307
1308\permanent\def\doifnotnumber#1%
1309  {\ifchknum#1\or
1310     \expandafter\gobbleoneargument
1311   \else
1312     \expandafter\firstofoneargument
1313   \fi}
1314
1315\aliased\let\doifnumberelse\doifelsenumber
1316
1317%D \macros
1318%D   {setpercentdimen}
1319%D
1320%D \starttyping
1321%D \scratchdimen=100pt \setpercentdimen\scratchdimen{10\letterpercent}
1322%D \scratchdimen=100pt \setpercentdimen\scratchdimen{5pt}
1323%D \scratchdimen       \percentdimen   \hsize       {10\letterpercent}
1324%D \stoptyping
1325
1326% todo: use the push back dimen trickery
1327
1328\permanent\def\percentdimen#1#2% dimen percentage (with %)
1329  {\dimexpr\clf_percentageof{#2}\dimexpr#1\relax}
1330
1331\permanent\protected\def\setpercentdimen#1#2% dimen percentage (with %)
1332  {#1=\clf_percentageof{#2}\dimexpr#1\relax}
1333
1334%D \macros
1335%D   {makerawcommalist,
1336%D    rawdoinsetelse,
1337%D    rawprocesscommalist,
1338%D    rawprocessaction}
1339%D
1340%D Some of the commands mentioned earlier are effective but slow. When one is
1341%D desperately in need of faster alternatives and when the conditions are
1342%D predictable safe, the \type {\raw} alternatives come into focus. A major drawback
1343%D is that they do not take \type {\c!constants} into account, simply because no
1344%D expansion is done. This is no problem with \type {\rawprocesscommalist}, because
1345%D this macro does not compare anything. Expandable macros are permitted as search
1346%D string.
1347%D
1348%D \starttyping
1349%D \makerawcommalist[string,string,...]\stringlist
1350%D \rawdoifelseinset{string}{string,...}{...}{...}
1351%D \rawprocesscommalist[string,string,...]\commando
1352%D \rawprocessaction[x][a=>\a,b=>\b,c=>\c]
1353%D \stoptyping
1354%D
1355%D Spaces embedded in the list, for instance after commas, spoil the search process.
1356%D The gain in speed depends on the length of the argument (the longer the argument,
1357%D the less we gain). The question is: do we still need these raw variants?
1358
1359\let\syst_helpers_do_make_raw_comma_list\gobbleoneargument
1360
1361\permanent\protected\def\makerawcommalist[#1]#2% use \processnext ... here
1362  {\scratchtoks\emptytoks
1363   \def\syst_helpers_do_make_raw_comma_list##1{\ifempty\scratchtoks\scratchtoks{##1}\else\toksapp\scratchtoks{,##1}\fi}%
1364   \processcommalist[#1]\syst_helpers_do_make_raw_comma_list
1365   \edef#2{\the\scratchtoks}}
1366
1367% beware: in mkiv { } were lost so it was not compatible with the non raw
1368
1369\aliased\let\rawprocesscommalist   \processcommalist    % can go
1370\aliased\let\rawprocesscommacommand\processcommacommand % can go
1371
1372%D Here is one without nesting .. still needed? Only used for modes (no nesting
1373%D there).
1374
1375\mutable\let\fastcommalistcommand\relax
1376
1377\protected\def\syst_helpers_process_fast_comma_item#1,%
1378  {\ifarguments
1379     \expandafter\syst_helpers_process_comma_item_gobble
1380   \or
1381     \fastcommalistcommand{#1}%
1382     \expandafter\syst_helpers_process_fast_comma_item_next
1383   \fi}
1384
1385\protected\def\syst_helpers_process_fast_comma_item_next
1386  {\expandafterspaces\syst_helpers_process_fast_comma_item}
1387
1388\permanent\protected\def\fastprocesscommalist[#1]#2%
1389  {\let\fastcommalistcommand#2%
1390   \expandafterspaces\syst_helpers_process_fast_comma_item#1\ignorearguments\ignorearguments\ignorearguments}
1391
1392\permanent\protected\def\fastprocesscommacommand[#1]#2%
1393  {\let\fastcommalistcommand#2%
1394   \normalexpanded{\noexpand\expandafterspaces\syst_helpers_process_fast_comma_item#1}\ignorearguments\ignorearguments\ignorearguments}
1395
1396% \def\rawdoifelseinset#1#2{\doifinstringelse{,#1,}{,#2,}}
1397% \def\rawdoifinset    #1#2{\doifinstring    {,#1,}{,#2,}}
1398
1399\def\syst_helpers_do_if_else_in_set#1%
1400  {\ifhasxtoks{,\m_syst_sub_string,}{,#1,}%
1401     \expandafter\firstoftwoarguments
1402   \else
1403     \expandafter\secondoftwoarguments
1404   \fi}
1405
1406\permanent\protected\def\rawdoifelseinset#1%
1407  {\edef\m_syst_sub_string{#1}% expand #1 here
1408   \ifempty\m_syst_sub_string
1409     \expandafter\thirdofthreearguments
1410   \else
1411     \expandafter\syst_helpers_do_if_else_in_set
1412   \fi}
1413
1414\aliased\let\rawdoifinsetelse\rawdoifelseinset
1415
1416\def\syst_helpers_do_if_in_set#1%
1417  {\ifhasxtoks{,\m_syst_sub_string,}{,#1,}%
1418     \expandafter\firstofoneargument
1419   \else
1420     \expandafter\gobbleoneargument
1421   \fi}
1422
1423\permanent\protected\def\rawdoifinset#1% or just alias this one
1424  {\edef\m_syst_sub_string{#1}% expand #1 here
1425   \ifx\m_syst_sub_string\m_syst_two_commas
1426     \expandafter\gobbletwoarguments
1427   \else
1428     \expandafter\syst_helpers_do_if_in_set
1429   \fi}
1430
1431%D Some more raw material:
1432
1433% probably never used
1434%
1435% \def\syst_helpers_raw_process_action#1=>#2,%
1436%   {\ifarguments
1437%     %\expandafter\syst_helpers_raw_process_action_gobble
1438%    \or
1439%      \expandafter\syst_helpers_raw_process_action_gobble
1440%    \or
1441%      \edef\m_syst_string_two{#1}%
1442%      \ifx\m_syst_string_one\m_syst_string_two
1443%        \def\m_syst_helpers_process_action{#2}%
1444%        \expandafter\expandafter\expandafter\syst_helpers_raw_process_action_gobble
1445%      \else
1446%        \ifx\s!unknown\m_syst_string_two
1447%          \def\m_syst_helpers_process_action_unknown{#2}%
1448%        \fi
1449%        \expandafter\expandafter\expandafter\syst_helpers_raw_process_action_next
1450%      \fi
1451%    \fi}
1452%
1453% \def\syst_helpers_raw_process_action_gobble#-\ignorearguments
1454%   {}
1455%
1456% \def\syst_helpers_raw_process_action_next
1457%   {\expandafterspaces\syst_helpers_raw_process_action}
1458%
1459% \permanent\protected\def\xrawprocessaction[#1]#*[#2]%
1460%   {\edef\m_syst_string_one{#1}%
1461%    \ifempty\m_syst_string_one
1462%       \let\m_syst_string_one\s!default
1463%    \fi
1464%    \let\m_syst_helpers_process_action\relax
1465%    \let\m_syst_helpers_process_action_unknown\relax
1466%    \syst_helpers_raw_process_action#2\ignorearguments\ignorearguments\ignorearguments
1467%    \ifrelax\m_syst_helpers_process_action
1468%      \m_syst_helpers_process_action_unknown
1469%    \else
1470%      \m_syst_helpers_process_action
1471%    \fi}
1472
1473%D When we process the list \type {a,b,c,d,e}, the raw routine takes over 30\% less
1474%D time, when we feed $20+$ character strings we gain about 20\%. Alternatives which
1475%D use \type {\futurelet} perform worse. Part of the speedup is due to the \type
1476%D {\let} and \type {\expandafter} in the test.
1477%D
1478%D \macros
1479%D   {dosetvalue,dosetevalue,dosetgvalue,docopyvalue,doresetvalue,
1480%D    dogetvalue}
1481%D
1482%D When we are going to do assignments, we have to take multi||linguality into account.
1483%D For the moment we keep things simple and single||lingual.
1484%D
1485%D \starttyping
1486%D \dosetvalue   {label}    {variable}   {value}
1487%D \dosetevalue  {label}    {variable}   {value}
1488%D \dosetgvalue  {label}    {variable}   {value}
1489%D \docopyvalue  {to label} {from label} {variable}
1490%D \doresetvalue {label}    {variable}
1491%D \stoptyping
1492%D
1493%D These macros are in fact auxiliary ones and are not meant for use outside the
1494%D assignment macros.
1495
1496\def\dosetvalue     #1#2{\defcsname        #1#2\endcsname} % takes #3
1497\def\dosetevalue    #1#2{\edefcsname       #1#2\endcsname} % takes #3
1498\def\dosetgvalue    #1#2{\global\edefcsname#1#2\endcsname} % takes #3
1499\def\doresetvalue   #1#2{\letcsname        #1#2\endcsname\empty}
1500\def\doignorevalue#1#2#3{\letcsname        #1#2\endcsname\empty}
1501\def\docopyvalue  #1#2#3{\defcsname        #1#3\endcsname{\csname#2#3\endcsname}}
1502
1503%D \macros
1504%D   {doassign,undoassign,doassignempty}
1505%D
1506%D Assignments are the backbone of \CONTEXT. Abhorred by the concept of style file
1507%D hacking, we took a considerable effort in building a parameterized system.
1508%D Unfortunately there is a price to pay in terms of speed. Compared to other
1509%D packages and taking the functionality of \CONTEXT\ into account, the total size
1510%D of the format file is still very acceptable. Now how are these assignments done.
1511%D
1512%D Assignments can be realized with:
1513%D
1514%D \starttyping
1515%D \doassign[label][variable=value]
1516%D \undoassign[label][variable=value]
1517%D \stoptyping
1518%D
1519%D and:
1520%D
1521%D \starttyping
1522%D \doassignempty[label][variable=value]
1523%D \stoptyping
1524%D
1525%D Assignments like \type{\doassign} are compatible with:
1526%D
1527%D \starttyping
1528%D \def\labelvariable{value}
1529%D \stoptyping
1530%D
1531%D We do check for the presence of an \type{=} and loudly complain of it's missed. We
1532%D will redefine this macro later on, when a more advanced message mechanism is
1533%D implemented.
1534
1535\protected\def\showassignerror#1#2%
1536  {\writestatus{setup}{missing or ungrouped '=' after '#1' in line #2}}
1537
1538\permanent\protected\def\doassignempty[#1][#2=#3]% kind of obsolete
1539  {\ifcsname#1#2\endcsname\else\dosetvalue{#1}{#2}{#3}\fi}
1540
1541%D \macros
1542%D   {getparameters,geteparameters,getgparameters,
1543%D    forgetparameters}
1544%D
1545%D Using the assignment commands directly is not our ideal of user friendly interfacing,
1546%D so we take some further steps.
1547%D
1548%D \starttyping
1549%D \getparameters    [label] [...=...,...=...]
1550%D \forgetparameters [label] [...=...,...=...]
1551%D \stoptyping
1552%D
1553%D Again, the label identifies the category a variable belongs to. The second argument
1554%D can be a comma separated list of assignments.
1555%D
1556%D \starttyping
1557%D \getparameters
1558%D   [demo]
1559%D   [alfa=1,
1560%D    beta=2]
1561%D \stoptyping
1562%D
1563%D is equivalent to
1564%D
1565%D \starttyping
1566%D \def\demoalfa{1}
1567%D \def\demobeta{2}
1568%D \stoptyping
1569%D
1570%D
1571%D In the pre||multi||lingual stadium \CONTEXT\ took the next approach. With
1572%D
1573%D \starttyping
1574%D \def\??demo {@@demo}
1575%D \def\!!alfa {alfa}
1576%D \def\!!beta {beta}
1577%D \stoptyping
1578%D
1579%D calling
1580%D
1581%D \starttyping
1582%D \getparameters
1583%D   [\??demo]
1584%D   [\!!alfa=1,
1585%D    \!!beta=2]
1586%D \stoptyping
1587%D
1588%D lead to:
1589%D
1590%D \starttyping
1591%D \def\@@demoalfa{1}
1592%D \def\@@demobeta{2}
1593%D \stoptyping
1594%D
1595%D Because we want to be able to distinguish the \type{!!} pre||tagged user supplied
1596%D variables from internal counterparts, we will introduce a slightly different tag
1597%D in the multi||lingual modules. There we will use \type{c!} or \type{v!},
1598%D depending on the context.
1599%D
1600%D By calling \type{doassign} directly, we save ourselves some argument passing
1601%D and gain some speed. Whatever optimizations we do, this command will always be
1602%D one of the bigger bottlenecks. The alternative \type{\geteparameters} --- it's
1603%D funny to see that this alternative saw the light so lately --- can be used to do
1604%D expanded assigments.
1605
1606% \mutable\lettonothing\currentvalue % only mkii
1607
1608% \permanent\protected\def\getparameters   {\dogetparameters\dosetvalue}
1609% \permanent\protected\def\geteparameters  {\dogetparameters\dosetevalue}
1610% \permanent\protected\def\getgparameters  {\dogetparameters\dosetgvalue}
1611% \permanent\protected\def\getxparameters  {\dogetparameters\dosetxvalue}
1612% \permanent\protected\def\forgetparameters{\dogetparameters\doignorevalue}
1613%
1614% \aliased\let\getexpandedparameters\geteparameters
1615%
1616% \def\syst_helpers_grab_parameter_error#1%
1617%   {\showassignerror{#1}{\the\inputlineno\space(\m_syst_parameter_n)}}
1618%
1619% \def\syst_helpers_grab_parameter#1,%
1620%   {\ifarguments
1621%      % done
1622%    \else
1623%      \syst_helpers_grab_parameter_okay#1,\ignorearguments
1624%      \expandafter\syst_helpers_grab_parameter_next
1625%    \fi}
1626%
1627% \def\syst_helpers_grab_parameter_okay#1=#2,%
1628%   {\ifarguments
1629%    \or
1630%      \syst_helpers_grab_parameter_error{#1}%
1631%    \or
1632%      \m_syst_parameter_s\m_syst_parameter_n{#1}{#2}%
1633%    \fi}
1634%
1635% \def\syst_helpers_grab_parameter_next
1636%   {\expandafterspaces\syst_helpers_grab_parameter}
1637%
1638% \permanent\protected\def\dogetparameters#1[#2]#*[#3]%
1639%   {\def\m_syst_parameter_n{#2}%
1640%    \let\m_syst_parameter_s#1%
1641%    \expandafterspaces\syst_helpers_grab_parameter#3\ignorearguments\ignorearguments}
1642
1643\tolerant\def\syst_get_parameters_n#*#1=#S#2,#M,%
1644  {\ifarguments             \expandafter\gobbleoneargument
1645   \or                      \mult_interfaces_get_parameters_error_indeed\m_syst_parameter{#1}%
1646   \orelse\ifparameter#2\or \defcsname\m_syst_parameter#1\endcsname{#2}%
1647   \else                    \letcsname\m_syst_parameter#1\endcsname\empty
1648   \fi                      \syst_get_parameters_n}
1649\tolerant\def\syst_get_parameters_e#*#1=#S#2,#M,%
1650  {\ifarguments             \expandafter\gobbleoneargument
1651   \or                      \mult_interfaces_get_parameters_error_indeed\m_syst_parameter{#1}%
1652   \orelse\ifparameter#2\or\edefcsname\m_syst_parameter#1\endcsname{#2}%
1653   \else                    \letcsname\m_syst_parameter#1\endcsname\empty
1654   \fi                      \syst_get_parameters_e}
1655\tolerant\def\syst_get_parameters_g#*#1=#S#2,#M,%
1656  {\ifarguments             \expandafter\gobbleoneargument
1657   \or                      \mult_interfaces_get_parameters_error_indeed\m_syst_parameter{#1}%
1658   \orelse\ifparameter#2\or\gdefcsname\m_syst_parameter#1\endcsname{#2}%
1659   \else                    \letcsname\m_syst_parameter#1\endcsname\empty
1660   \fi                      \syst_get_parameters_g}
1661\tolerant\def\syst_get_parameters_x#*#1=#S#2,#M,%
1662  {\ifarguments             \expandafter\gobbleoneargument
1663   \or                      \mult_interfaces_get_parameters_error_indeed\m_syst_parameter{#1}%
1664   \orelse\ifparameter#2\or\xdefcsname\m_syst_parameter#1\endcsname{#2}%
1665   \else                    \letcsname\m_syst_parameter#1\endcsname\empty
1666   \fi                      \syst_get_parameters_x}
1667\tolerant\def\syst_get_parameters_f#*#1=#S#2,#M,%
1668  {\ifarguments             \expandafter\gobbleoneargument
1669   \or                      \mult_interfaces_get_parameters_error_indeed\m_syst_parameter{#1}%
1670   \else                    \letcsname\m_syst_parameter#1\endcsname\empty
1671   \fi                      \syst_get_parameters_f}
1672
1673\permanent\protected\def\getparameters   [#1]#*[#S#2]{\cdef\m_syst_parameter{#1}\syst_get_parameters_n#2\ignorearguments\ignorearguments}
1674\permanent\protected\def\geteparameters  [#1]#*[#S#2]{\cdef\m_syst_parameter{#1}\syst_get_parameters_e#2\ignorearguments\ignorearguments}
1675\permanent\protected\def\getgparameters  [#1]#*[#S#2]{\cdef\m_syst_parameter{#1}\syst_get_parameters_g#2\ignorearguments\ignorearguments}
1676\permanent\protected\def\getxparameters  [#1]#*[#S#2]{\cdef\m_syst_parameter{#1}\syst_get_parameters_x#2\ignorearguments\ignorearguments}
1677\permanent\protected\def\forgetparameters[#1]#*[#S#2]{\cdef\m_syst_parameter{#1}\syst_get_parameters_f#2\ignorearguments\ignorearguments}
1678
1679\aliased\let\getexpandedparameters\geteparameters
1680
1681%D \macros
1682%D   {getemptyparameters}
1683%D
1684%D Sometimes we explicitly want variables to default to an empty string, so we
1685%D welcome:
1686%D
1687%D \starttyping
1688%D \getemptyparameters [label] [...=...,...=...]
1689%D \stoptyping
1690
1691% \let\syst_helpers_get_empty_parameters\gobbleoneargument
1692%
1693% \permanent\protected\def\getemptyparameters[#1]#*[#2]%
1694%   {\def\syst_helpers_get_empty_parameters##1{\doassignempty[#1][##1]}%
1695%    \processcommalist[#2]\syst_helpers_get_empty_parameters}
1696
1697\let\syst_helpers_copy_parameter\relax
1698
1699\permanent\protected\def\getemptyparameters[#1]#*[#2]%
1700  {\def\syst_helpers_copy_parameter##1{\letcsname#1##1\endcsname\empty}%
1701   \processcommalist[#2]\syst_helpers_get_empty_parameters}
1702
1703%D We now just alias these as there is no need for a speedup. These have not been used
1704%D for a long time in core code.
1705
1706\aliased\let\doassign  \getparameters    % obsolete, these will go
1707\aliased\let\doeassign \geteparameters   % obsolete, these will go
1708\aliased\let\undoassign\forgetparameters % obsolete, these will go
1709
1710%D \macros
1711%D   {processassignmentlist,processassignmentcommand,
1712%D    startprocessassignmentlist,startprocessassignmentcommand}
1713%D
1714%D For Wolfgang:
1715%D
1716%D \starttyping
1717%D \def\showpair#1#2{key:#1, value:#2\par}
1718%D \processassignmentlist[a=1,b=2]\showpair
1719%D \stoptyping
1720%D
1721%D We can optimize this one if needed but it's not a core macro so hardly worth the
1722%D trouble and tokens.
1723
1724\mutable\let\currentassignmentlistcommand\gobbletwoarguments
1725
1726\mutable\lettonothing\currentassignmentlistkey
1727\mutable\lettonothing\currentassignmentlistvalue
1728
1729\let\syst_helpers_process_assign_entry\gobbleoneargument
1730
1731% \permanent\protected\def\processassignmentlist[#1]#2% #2 == \command{key}{value]
1732%   {\def\syst_helpers_process_assign_entry##1{#2}% {##2}{##3} % namespace is ignored
1733%    \dogetparameters\syst_helpers_process_assign_entry[][#1]}
1734
1735\tolerant\def\syst_helpers_process_assign_value#*#1=#S#2,#M,%
1736  {\ifarguments
1737     \expandafter\gobbleoneargument
1738   \or
1739     \mult_interfaces_get_parameters_error_indeed\empty{#1}%
1740   \else
1741     \syst_helpers_process_assign_entry{#1}{#2}%
1742   \fi
1743   \syst_helpers_process_assign_value}
1744
1745\permanent\protected\def\processassignmentlist[#S#1]#2% #2 == \command{key}{value]
1746  {\def\syst_helpers_process_assign_entry{#2}%
1747   \syst_helpers_process_assign_value#1\ignorearguments\ignorearguments}
1748
1749\permanent\protected\def\processassignmentcommand[#1]%
1750  {\normalexpanded{\processassignmentlist[#1]}}
1751
1752\permanent\protected\def\startprocessassignmentlist[#1]#2\stopprocessassignmentlist
1753  {\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}%
1754   \processassignmentlist[#1]\currentassignmentlistcommand}
1755
1756\permanent\protected\lettonothing\stopprocessassignmentlist
1757
1758\permanent\protected\def\startprocessassignmentcommand[#1]#2\stopprocessassignmentcommand
1759  {\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}%
1760   \normalexpanded{\processassignmentlist[#1]}\currentassignmentlistcommand}
1761
1762\permanent\protected\lettonothing\stopprocessassignmentcommand
1763
1764%D \macros
1765%D   {currentvalue}
1766%D
1767%D Just in case a \type{\getparameter} argument itself ends up inside a \type
1768%D {\write} or other expandable location, our new macro needs a default value.
1769%D
1770%D \starttyping
1771%D \getparameters[xxx][aaa=bbb]\par
1772%D \getparameters[xxx][=bbb]\par
1773%D \getparameters[xxx][aaa=]\par
1774%D \getparameters[xxx][=]\par
1775%D \getparameters[xxx][aaa]\par
1776%D \stoptyping
1777
1778%D \macros
1779%D   {copyparameters}
1780%D
1781%D Some \CONTEXT\ commands take their default setups from others. All commands that
1782%D are able to provide backgounds or rules around some content, for instance default
1783%D to the standard command for ruled boxes. Is situations like this we can use:
1784%D
1785%D \starttyping
1786%D \copyparameters [to-label] [from-label] [name1,name2,...]
1787%D \stoptyping
1788%D
1789%D For instance
1790%D
1791%D \starttyping
1792%D \copyparameters
1793%D   [internal][external]
1794%D   [alfa,beta]
1795%D \stoptyping
1796%D
1797%D Leads to:
1798%D
1799%D \starttyping
1800%D \def\internalalfa {\externalalfa}
1801%D \def\internalbeta {\externalbeta}
1802%D \stoptyping
1803%D
1804%D By using \type {\docopyvalue} we've prepared this command for use in a
1805%D multi||lingual environment.
1806
1807% \let\syst_helpers_copy_parameter\relax
1808%
1809% \permanent\protected\def\copyparameters[#1]#*[#2]#*[#3]%
1810%   {\iftok{#1}{#2}\else
1811%      \def\syst_helpers_copy_parameter{\docopyvalue{#1}{#2}}% ##1
1812%      \processcommalist[#3]\syst_helpers_copy_parameter
1813%    \fi}
1814
1815\let\syst_helpers_copy_parameter\relax
1816
1817\permanent\protected\def\copyparameters[#1]#*[#2]#*[#3]%
1818  {\iftok{#1}{#2}\else
1819     \def\syst_helpers_copy_parameter##1{\defcsname#1##1\endcsname{\begincsname#2##1\endcsname}}%
1820     \processcommalist[#3]\syst_helpers_copy_parameter
1821   \fi}
1822
1823% %D \macros
1824% %D   {ifparameters,checkparameters}
1825% %D
1826% %D A slightly different one is \type {\checkparameters}, which also checks on the
1827% %D presence of a~\type {=}.
1828% %D
1829% %D The boolean \type {\ifparameters} can be used afterwards. Combining both in one
1830% %D \type {\if}||macro would lead to problems with nested \type {\if}'s.
1831% %D
1832% %D \starttyping
1833% %D \checkparameters[argument]
1834% %D \stoptyping
1835
1836% \newif\ifparameters
1837
1838% \protected\def\checkparameters[#1]%
1839%   {\ifhastok={#1}\parameterstrue\else\parametersfalse\fi}
1840
1841%D \macros
1842%D   {getfromcommalist,getfromcommacommand,
1843%D    commalistelement,
1844%D    getcommalistsize,getcommacommandsize}
1845%D
1846%D It's possible to get an element from a commalist or a command representing
1847%D a commalist.
1848%D
1849%D \starttyping
1850%D \getfromcommalist    [string] [n]
1851%D \getfromcommacommand [string,\strings,string,...] [n]
1852%D \stoptyping
1853%D
1854%D The difference betwee the two of them is the same as the difference between
1855%D \type {\processcomma...}. The found string is stored in \type
1856%D {\commalistelement}.
1857%D
1858%D We can calculate the size of a comma separated list by using:
1859%D
1860%D \starttyping
1861%D \getcommalistsize    [string,string,...]
1862%D \getcommacommandsize [string,\strings,string,...]
1863%D \stoptyping
1864%D
1865%D Afterwards, the length is available in the macro \type {\commalistsize}
1866%D (not a \COUNTER).
1867
1868\newinteger\commalistcounter
1869
1870\mutable\def\commalistsize{0}
1871
1872\def\syst_helpers_get_comma_list_size#0,% no #- as we need to count
1873  {\ifarguments\or
1874     \advanceby\commalistcounter\plusone
1875     \expandafter\syst_helpers_get_comma_list_size
1876   \fi}
1877
1878\permanent\protected\def\getcommalistsize[%
1879  {\futureexpand]\syst_helpers_get_comma_list_size_nop\syst_helpers_get_comma_list_size_yes}
1880
1881\def\syst_helpers_get_comma_list_size_yes#+]%
1882  {\commalistcounter\zerocount
1883   \syst_helpers_get_comma_list_size #1,\ignorearguments\ignorearguments
1884   \edef\commalistsize{\the\commalistcounter}}
1885
1886\def\syst_helpers_get_comma_list_size_nop]%
1887  {\commalistcounter\zerocount
1888   \let\commalistsize\!!zerocount}
1889
1890\permanent\protected\def\getcommacommandsize[#1]%
1891  {\normalexpanded{\getcommalistsize[#1]}}
1892
1893\permanent\def\getcommalistcount[#1]%
1894  {\beginlocalcontrol
1895   \getcommalistsize[#1]%
1896   \endlocalcontrol
1897   \the\commalistcounter}
1898
1899\permanent\def\getcommacommandcount[#1]%
1900  {\beginlocalcontrol
1901   \getcommacommandsize[#1]%
1902   \endlocalcontrol
1903   \the\commalistcounter}
1904
1905%D Filters:
1906
1907% \def\syst_helpers_gobble_comma_list#0\ignorearguments{}
1908
1909\def\syst_helpers_gobble_comma_list#-\ignorearguments{}
1910
1911\def\syst_helpers_get_from_comma_list#1,%
1912  {\ifarguments \or
1913     \advanceby\commalistcounter \minusone
1914     \ifcase\commalistcounter
1915       \def\commalistelement{#1}%
1916       \expandafter\expandafter\expandafter\syst_helpers_gobble_comma_list
1917     \else
1918       \expandafter\expandafter\expandafter\syst_helpers_get_from_comma_list_next
1919     \fi
1920   \fi}
1921
1922\def\syst_helpers_get_from_comma_list_next
1923  {\expandafterspaces\syst_helpers_get_from_comma_list}
1924
1925\permanent\protected\def\getfromcommalist[#1]#*[#2]%
1926  {\lettonothing\commalistelement
1927   \commalistcounter#2\relax
1928   \expandafterspaces\syst_helpers_get_from_comma_list#1\ignorearguments\ignorearguments}
1929
1930\permanent\protected\def\getfromcommacommand[#1]%
1931  {\normalexpanded{\getfromcommalist[#1]}}
1932
1933%D Watertight (and efficient) solutions are hard to find, due to the handling of
1934%D braces during parameters passing and scanning. Nevertheless:
1935%D
1936%D \startbuffer
1937%D \def\dosomething#1{(#1=\commalistsize) }
1938%D
1939%D \getcommalistsize [\hbox{$a,b,c,d,e,f$}] \dosomething 1
1940%D \getcommalistsize [{a,b,c,d,e,f}]        \dosomething 1
1941%D \getcommalistsize [{a,b,c},d,e,f]        \dosomething 4
1942%D \getcommalistsize [a,b,{c,d,e},f]        \dosomething 4
1943%D \getcommalistsize [a{b,c},d,e,f]         \dosomething 4
1944%D \getcommalistsize [{a,b}c,d,e,f]         \dosomething 4
1945%D \getcommalistsize []                     \dosomething 0
1946%D \getcommalistsize [{[}]                  \dosomething 1
1947%D \stopbuffer
1948%D
1949%D \typebuffer
1950%D
1951%D reports:
1952%D
1953%D \getbuffer
1954
1955%D \macros
1956%D   {dogetcommalistelement,dogetcommacommandelement}
1957%D
1958%D For low level (fast) purposes, we can also use the next alternative, which can
1959%D handle 9~elements at most.
1960%D
1961%D \starttyping
1962%D \dogetcommalistelement1\from a,b,c\to\commalistelement
1963%D \stoptyping
1964
1965\def\syst_helpers_get_comma_list_element#1,#2,#3,#4,#5,#6,#7,#8,#9,%
1966  {\ifcase\scratchcounter\or#1\or#2\or#3\or#4\or#5\or#6\or#7\or#8\or#9\fi
1967   \syst_helpers_gobble_comma_list}
1968
1969\permanent\protected\def\dogetcommacommandelement#1\from#2\to#3%
1970  {\scratchcounter#1\relax
1971   \edef#3{\normalexpanded{\syst_helpers_get_comma_list_element#2\ignorearguments\ignorearguments}}}
1972
1973%D \macros
1974%D   {dosingleargument,dodoubleargument,dotripleargument,
1975%D    doquadrupleargument,doquintupleargument,dosixtupleargument,
1976%D    doseventupleargument}
1977%D
1978%D When working with delimited arguments, spaces and lineendings can interfere. The
1979%D next set of macros uses \TEX' internal scanner for grabbing everything between
1980%D arguments. Forgive me the funny names.
1981%D
1982%D \starttyping
1983%D \dosingleargument    \command = \command[#1]
1984%D \dodoubleargument    \command = \command[#1][#2]
1985%D \dotripleargument    \command = \command[#1][#2][#3]
1986%D \doquadrupleargument \command = \command[#1][#2][#3][#4]
1987%D \doquintupleargument \command = \command[#1][#2][#3][#4][#5]
1988%D \dosixtupleargument  \command = \command[#1][#2][#3][#4][#5][#6]
1989%D \doseventupleargument\command = \command[#1][#2][#3][#4][#5][#6][#7]
1990%D \stoptyping
1991%D
1992%D These macros can be used in the following way:
1993%D
1994%D \starttyping
1995%D \def\dosetupsomething[#1][#2]%
1996%D   {... #1 ... #2 ...}
1997%D
1998%D \protected\def\setupsomething
1999%D   {\dodoubleargument\dosetupsomething}
2000%D \stoptyping
2001%D
2002%D The implementation can be surprisingly simple and needs no further explanation,
2003%D like:
2004%D
2005%D \starttyping
2006%D \def\dosingleargument#1[#2]%
2007%D   {#1[#2]}
2008%D \def\dotripleargument#1[#2]#3[#4]#5[#6]%
2009%D   {#1[#2][#4][#6]}
2010%D \def\doquintupleargument#1%
2011%D   {\def\dodoquintupleargument[##1]##2[##3]##4[##5]##6[##7]##8[##9]%
2012%D      {#1[##1][##3][##5][##7][##9]}%
2013%D    \dodoquintupleargument}
2014%D \stoptyping
2015%D
2016%D Because \TEX\ accepts 9~arguments at most, we have to use two||step solution when
2017%D getting five or more arguments.
2018%D
2019%D When developing more and more of the real \CONTEXT, we started using some
2020%D alternatives that provided empty arguments (in fact optional ones) whenever the
2021%D user failed to supply them. Because this more complicated macros enable us to do
2022%D some checking, we reimplemented the non||empty ones.
2023
2024%D \macros
2025%D   {iffirstagument,ifsecondargument,ifthirdargument,
2026%D    iffourthargument,iffifthargument,ifsixthargument,
2027%D    ifseventhargument}
2028%D
2029%D We use some signals for telling the calling macros if all wanted arguments are
2030%D indeed supplied by the user.
2031
2032\newinteger\nofarguments
2033
2034\newif\iffirstargument
2035\newif\ifsecondargument
2036\newif\ifthirdargument
2037\newif\iffourthargument
2038\newif\iffifthargument
2039\newif\ifsixthargument
2040\newif\ifseventhargument
2041
2042%D \macros
2043%D   {dosingleempty,dodoubleempty,dotripleempty,
2044%D    doquadrupleempty,doquintupleempty,dosixtupeempty,
2045%D    doseventupleempty}
2046%D
2047%D The empty argument supplying macros mentioned before, look like:
2048%D
2049%D \starttyping
2050%D \dosingleempty    \command
2051%D \dodoubleempty    \command
2052%D \dotripleempty    \command
2053%D \doquadrupleempty \command
2054%D \doquintupleempty \command
2055%D \dosixtuple_empty  \command
2056%D \doseventupleempty\command
2057%D \stoptyping
2058%D
2059%D So \type{\dodoubleempty} leads to:
2060%D
2061%D \starttyping
2062%D \command[#1][#2]
2063%D \command[#1][]
2064%D \command[][]
2065%D \stoptyping
2066%D
2067%D Depending of the generousity of the user. Afterwards one can use the \type
2068%D {\if...argument} boolean. For novice: watch the stepwise doubling of \type {#}'s.
2069
2070%D NB : experimental versions in cont-exp.mkiv
2071
2072%D Common:
2073
2074\newtoks\t_syst_aux
2075
2076% \def\syst_helpers_single_empty_one_yes      {\firstargumenttrue  \the\t_syst_aux}
2077% \def\syst_helpers_double_empty_two_yes      {\secondargumenttrue \the\t_syst_aux}
2078% \def\syst_helpers_triple_empty_three_yes    {\thirdargumenttrue  \the\t_syst_aux}
2079% \def\syst_helpers_quadruple_empty_four_yes  {\fourthargumenttrue \the\t_syst_aux}
2080% \def\syst_helpers_quintuple_empty_five_yes  {\fifthargumenttrue  \the\t_syst_aux}
2081% \def\syst_helpers_sixtuple_empty_six_yes    {\sixthargumenttrue  \the\t_syst_aux}
2082% \def\syst_helpers_seventuple_empty_seven_yes{\seventhargumenttrue\the\t_syst_aux}
2083%
2084% %D Single:
2085%
2086% \protected\def\dosingleempty#1%
2087%   {\t_syst_aux{#1}%
2088%    \futureexpand[\syst_helpers_single_empty_one_yes\syst_helpers_single_empty_one_nop}
2089%
2090% \def\syst_helpers_single_empty_one_nop
2091%   {\firstargumentfalse
2092%    \the\t_syst_aux[]}
2093%
2094% %D Double
2095%
2096% \protected\def\dodoubleempty#1%
2097%   {\t_syst_aux{#1}%
2098%    \futureexpand[\syst_helpers_double_empty_one_yes\syst_helpers_double_empty_one_nop}
2099%
2100% % \def\syst_helpers_double_empty_one_yes[#1]%
2101% %   {\firstargumenttrue
2102% %    \toksapp\t_syst_aux{[{#1}]}%
2103% %    \futureexpand[\syst_helpers_double_empty_two_yes\syst_helpers_double_empty_two_nop}
2104% %
2105% % nicer is:
2106%
2107% \def\syst_helpers_double_empty_one_yes[#+]%
2108%   {\firstargumenttrue
2109%    \toksapp\t_syst_aux{[#1]}%
2110%    \futureexpand[\syst_helpers_double_empty_two_yes\syst_helpers_double_empty_two_nop}
2111%
2112% \def\syst_helpers_double_empty_one_nop
2113%   {\firstargumentfalse
2114%    \secondargumentfalse
2115%    \the\t_syst_aux[][]}
2116%
2117% \def\syst_helpers_double_empty_two_nop
2118%   {\secondargumentfalse
2119%    \the\t_syst_aux[]}
2120%
2121% % Triple
2122%
2123% \protected\def\dotripleempty#1%
2124%   {\t_syst_aux{#1}%
2125%    \futureexpand[\syst_helpers_triple_empty_one_yes\syst_helpers_triple_empty_one_nop}
2126%
2127% \def\syst_helpers_triple_empty_one_yes[#+]%
2128%   {\firstargumenttrue
2129%    \toksapp\t_syst_aux{[#1]}%
2130%    \futureexpand[\syst_helpers_triple_empty_two_yes\syst_helpers_triple_empty_two_nop}
2131%
2132% \def\syst_helpers_triple_empty_two_yes[#+]%
2133%   {\secondargumenttrue
2134%    \toksapp\t_syst_aux{[#1]}%
2135%    \futureexpand[\syst_helpers_triple_empty_three_yes\syst_helpers_triple_empty_three_nop}
2136%
2137% \def\syst_helpers_triple_empty_one_nop
2138%   {\firstargumentfalse
2139%    \secondargumentfalse
2140%    \thirdargumentfalse
2141%    \the\t_syst_aux[][][]}
2142%
2143% \def\syst_helpers_triple_empty_two_nop
2144%   {\secondargumentfalse
2145%    \thirdargumentfalse
2146%    \the\t_syst_aux[][]}
2147%
2148% \def\syst_helpers_triple_empty_three_nop
2149%   {\thirdargumentfalse
2150%    \the\t_syst_aux[]}
2151%
2152% %D Quadruple:
2153%
2154% \protected\def\doquadrupleempty#1%
2155%   {\t_syst_aux{#1}%
2156%    \futureexpand[\syst_helpers_quadruple_empty_one_yes\syst_helpers_quadruple_empty_one_nop}
2157%
2158% \def\syst_helpers_quadruple_empty_one_yes[#+]%
2159%   {\firstargumenttrue
2160%    \toksapp\t_syst_aux{[#1]}%
2161%    \futureexpand[\syst_helpers_quadruple_empty_two_yes\syst_helpers_quadruple_empty_two_nop}
2162%
2163% \def\syst_helpers_quadruple_empty_two_yes[#+]%
2164%   {\secondargumenttrue
2165%    \toksapp\t_syst_aux{[#1]}%
2166%    \futureexpand[\syst_helpers_quadruple_empty_three_yes\syst_helpers_quadruple_empty_three_nop}
2167%
2168% \def\syst_helpers_quadruple_empty_three_yes[#+]%
2169%   {\thirdargumenttrue
2170%    \toksapp\t_syst_aux{[#1]}%
2171%    \futureexpand[\syst_helpers_quadruple_empty_four_yes\syst_helpers_quadruple_empty_four_nop}
2172%
2173% \def\syst_helpers_quadruple_empty_one_nop
2174%   {\firstargumentfalse
2175%    \secondargumentfalse
2176%    \thirdargumentfalse
2177%    \fourthargumentfalse
2178%    \the\t_syst_aux[][][][]}
2179%
2180% \def\syst_helpers_quadruple_empty_two_nop
2181%   {\secondargumentfalse
2182%    \thirdargumentfalse
2183%    \fourthargumentfalse
2184%    \the\t_syst_aux[][][]}
2185%
2186% \def\syst_helpers_quadruple_empty_three_nop
2187%   {\thirdargumentfalse
2188%    \fourthargumentfalse
2189%    \the\t_syst_aux[][]}
2190%
2191% \def\syst_helpers_quadruple_empty_four_nop
2192%   {\fourthargumentfalse
2193%    \the\t_syst_aux[]}
2194%
2195% %D Quintuple:
2196%
2197% \protected\def\doquintupleempty#1%
2198%   {\t_syst_aux{#1}%
2199%    \futureexpand[\syst_helpers_quintuple_empty_one_yes\syst_helpers_quintuple_empty_one_nop}
2200%
2201% \def\syst_helpers_quintuple_empty_one_yes[#+]%
2202%   {\firstargumenttrue
2203%    \toksapp\t_syst_aux{[#1]}%
2204%    \futureexpand[\syst_helpers_quintuple_empty_two_yes\syst_helpers_quintuple_empty_two_nop}
2205%
2206% \def\syst_helpers_quintuple_empty_two_yes[#+]%
2207%   {\secondargumenttrue
2208%    \toksapp\t_syst_aux{[#1]}%
2209%    \futureexpand[\syst_helpers_quintuple_empty_three_yes\syst_helpers_quintuple_empty_three_nop}
2210%
2211% \def\syst_helpers_quintuple_empty_three_yes[#+]%
2212%   {\thirdargumenttrue
2213%    \toksapp\t_syst_aux{[#1]}%
2214%    \futureexpand[\syst_helpers_quintuple_empty_four_yes\syst_helpers_quintuple_empty_four_nop}
2215%
2216% \def\syst_helpers_quintuple_empty_four_yes[#+]%
2217%   {\fourthargumenttrue
2218%    \toksapp\t_syst_aux{[#1]}%
2219%    \futureexpand[\syst_helpers_quintuple_empty_five_yes\syst_helpers_quintuple_empty_five_nop}
2220%
2221% \def\syst_helpers_quintuple_empty_one_nop
2222%   {\firstargumentfalse
2223%    \secondargumentfalse
2224%    \thirdargumentfalse
2225%    \fourthargumentfalse
2226%    \fifthargumentfalse
2227%    \the\t_syst_aux[][][][][]}
2228%
2229% \def\syst_helpers_quintuple_empty_two_nop
2230%   {\secondargumentfalse
2231%    \thirdargumentfalse
2232%    \fourthargumentfalse
2233%    \fifthargumentfalse
2234%    \the\t_syst_aux[][][][]}
2235%
2236% \def\syst_helpers_quintuple_empty_three_nop
2237%   {\thirdargumentfalse
2238%    \fourthargumentfalse
2239%    \fifthargumentfalse
2240%    \the\t_syst_aux[][][]}
2241%
2242% \def\syst_helpers_quintuple_empty_four_nop
2243%   {\fourthargumentfalse
2244%    \fifthargumentfalse
2245%    \the\t_syst_aux[][]}
2246%
2247% \def\syst_helpers_quintuple_empty_five_nop
2248%   {\fifthargumentfalse
2249%    \the\t_syst_aux[]}
2250%
2251% %D Sixtuple:
2252%
2253% \protected\def\dosixtupleempty#1%
2254%   {\t_syst_aux{#1}%
2255%    \futureexpand[\syst_helpers_sixtuple_empty_one_yes\syst_helpers_sixtuple_empty_one_nop}
2256%
2257% \def\syst_helpers_sixtuple_empty_one_yes[#+]%
2258%   {\firstargumenttrue
2259%    \toksapp\t_syst_aux{[#1]}%
2260%    \futureexpand[\syst_helpers_sixtuple_empty_two_yes\syst_helpers_sixtuple_empty_two_nop}
2261%
2262% \def\syst_helpers_sixtuple_empty_two_yes[#+]%
2263%   {\secondargumenttrue
2264%    \toksapp\t_syst_aux{[#1]}%
2265%    \futureexpand[\syst_helpers_sixtuple_empty_three_yes\syst_helpers_sixtuple_empty_three_nop}
2266%
2267% \def\syst_helpers_sixtuple_empty_three_yes[#+]%
2268%   {\thirdargumenttrue
2269%    \toksapp\t_syst_aux{[#1]}%
2270%    \futureexpand[\syst_helpers_sixtuple_empty_four_yes\syst_helpers_sixtuple_empty_four_nop}
2271%
2272% \def\syst_helpers_sixtuple_empty_four_yes[#+]%
2273%   {\fourthargumenttrue
2274%    \toksapp\t_syst_aux{[#1]}%
2275%    \futureexpand[\syst_helpers_sixtuple_empty_five_yes\syst_helpers_sixtuple_empty_five_nop}
2276%
2277% \def\syst_helpers_sixtuple_empty_five_yes[#+]%
2278%   {\fifthargumenttrue
2279%    \toksapp\t_syst_aux{[#1]}%
2280%    \futureexpand[\syst_helpers_sixtuple_empty_six_yes\syst_helpers_sixtuple_empty_six_nop}
2281%
2282% \def\syst_helpers_sixtuple_empty_one_nop
2283%   {\firstargumentfalse
2284%    \secondargumentfalse
2285%    \thirdargumentfalse
2286%    \fourthargumentfalse
2287%    \fifthargumentfalse
2288%    \sixthargumentfalse
2289%    \the\t_syst_aux[][][][][][]}
2290%
2291% \def\syst_helpers_sixtuple_empty_two_nop
2292%   {\secondargumentfalse
2293%    \thirdargumentfalse
2294%    \fourthargumentfalse
2295%    \fifthargumentfalse
2296%    \sixthargumentfalse
2297%    \the\t_syst_aux[][][][][]}
2298%
2299% \def\syst_helpers_sixtuple_empty_three_nop
2300%   {\thirdargumentfalse
2301%    \fourthargumentfalse
2302%    \fifthargumentfalse
2303%    \sixthargumentfalse
2304%    \the\t_syst_aux[][][][]}
2305%
2306% \def\syst_helpers_sixtuple_empty_four_nop
2307%   {\fourthargumentfalse
2308%    \fifthargumentfalse
2309%    \sixthargumentfalse
2310%    \the\t_syst_aux[][][]}
2311%
2312% \def\syst_helpers_sixtuple_empty_five_nop
2313%   {\fifthargumentfalse
2314%    \sixthargumentfalse
2315%    \the\t_syst_aux[][]}
2316%
2317% \def\syst_helpers_sixtuple_empty_six_nop
2318%   {\sixthargumentfalse
2319%    \the\t_syst_aux[]}
2320%
2321% %D Seventuple:
2322%
2323% \protected\def\doseventupleempty#1%
2324%   {\t_syst_aux{#1}%
2325%    \futureexpand[\syst_helpers_seventuple_empty_one_yes\syst_helpers_seventuple_empty_one_nop}
2326%
2327% \def\syst_helpers_seventuple_empty_one_yes[#+]%
2328%   {\firstargumenttrue
2329%    \toksapp\t_syst_aux{[#1]}%
2330%    \futureexpand[\syst_helpers_seventuple_empty_two_yes\syst_helpers_seventuple_empty_two_nop}
2331%
2332% \def\syst_helpers_seventuple_empty_two_yes[#+]%
2333%   {\secondargumenttrue
2334%    \toksapp\t_syst_aux{[#1]}%
2335%    \futureexpand[\syst_helpers_seventuple_empty_three_yes\syst_helpers_seventuple_empty_three_nop}
2336%
2337% \def\syst_helpers_seventuple_empty_three_yes[#+]%
2338%   {\thirdargumenttrue
2339%    \toksapp\t_syst_aux{[#1]}%
2340%    \futureexpand[\syst_helpers_seventuple_empty_four_yes\syst_helpers_seventuple_empty_four_nop}
2341%
2342% \def\syst_helpers_seventuple_empty_four_yes[#+]%
2343%   {\fourthargumenttrue
2344%    \toksapp\t_syst_aux{[#1]}%
2345%    \futureexpand[\syst_helpers_seventuple_empty_five_yes\syst_helpers_seventuple_empty_five_nop}
2346%
2347% \def\syst_helpers_seventuple_empty_five_yes[#+]%
2348%   {\fifthargumenttrue
2349%    \toksapp\t_syst_aux{[#1]}%
2350%    \futureexpand[\syst_helpers_seventuple_empty_six_yes\syst_helpers_seventuple_empty_six_nop}
2351%
2352% \def\syst_helpers_seventuple_empty_six_yes[#+]%
2353%   {\sixthargumenttrue
2354%    \toksapp\t_syst_aux{[#1]}%
2355%    \futureexpand[\syst_helpers_seventuple_empty_seven_yes\syst_helpers_seventuple_empty_seven_nop}
2356%
2357% \def\syst_helpers_seventuple_empty_one_nop
2358%   {\firstargumentfalse
2359%    \secondargumentfalse
2360%    \thirdargumentfalse
2361%    \fourthargumentfalse
2362%    \fifthargumentfalse
2363%    \sixthargumentfalse
2364%    \seventhargumentfalse
2365%    \the\t_syst_aux[][][][][][][]}
2366%
2367% \def\syst_helpers_seventuple_empty_two_nop
2368%   {\secondargumentfalse
2369%    \thirdargumentfalse
2370%    \fourthargumentfalse
2371%    \fifthargumentfalse
2372%    \sixthargumentfalse
2373%    \seventhargumentfalse
2374%    \the\t_syst_aux[][][][][][]}
2375%
2376% \def\syst_helpers_seventuple_empty_three_nop
2377%   {\thirdargumentfalse
2378%    \fourthargumentfalse
2379%    \fifthargumentfalse
2380%    \sixthargumentfalse
2381%    \seventhargumentfalse
2382%    \the\t_syst_aux[][][][][]}
2383%
2384% \def\syst_helpers_seventuple_empty_four_nop
2385%   {\fourthargumentfalse
2386%    \fifthargumentfalse
2387%    \sixthargumentfalse
2388%    \seventhargumentfalse
2389%    \the\t_syst_aux[][][][]}
2390%
2391% \def\syst_helpers_seventuple_empty_five_nop
2392%   {\fifthargumentfalse
2393%    \sixthargumentfalse
2394%    \seventhargumentfalse
2395%    \the\t_syst_aux[][][]}
2396%
2397% \def\syst_helpers_seventuple_empty_six_nop
2398%   {\sixthargumentfalse
2399%    \seventhargumentfalse
2400%    \the\t_syst_aux[][]}
2401%
2402% \def\syst_helpers_seventuple_empty_seven_nop
2403%   {\seventhargumentfalse
2404%    \the\t_syst_aux[]}
2405
2406\tolerant\def\syst_single_empty#1\relax[#S#2]%
2407  {\ifarguments
2408   \or\firstargumentfalse
2409   \or\firstargumenttrue
2410   \fi
2411   #1[#2]}
2412
2413\tolerant\def\syst_double_empty#1\relax[#S#+]#*[#S#+]%
2414  {\ifarguments
2415   \or\firstargumentfalse\secondargumentfalse
2416   \or\firstargumenttrue \secondargumentfalse
2417   \or\firstargumenttrue \secondargumenttrue
2418   \fi
2419   #1[#2][#3]}
2420
2421\tolerant\def\syst_triple_empty#1\relax[#S#+]#*[#S#+]#*[#S#+]%
2422  {\ifarguments
2423   \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse
2424   \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse
2425   \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse
2426   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue
2427   \fi
2428   #1[#2][#3][#4]}
2429
2430\tolerant\def\syst_quadruple_empty#1\relax[#S#+]#*[#S#+]#*[#S#+]#*[#S#+]%
2431  {\ifarguments
2432   \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse
2433   \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse
2434   \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse
2435   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse
2436   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue
2437   \fi
2438   #1[#2][#3][#4][#5]}
2439
2440\tolerant\def\syst_quintuple_empty#1\relax[#S#+]#*[#S#+]#*[#S#+]#*[#S#+]#*[#S#+]%
2441  {\ifarguments
2442   \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse
2443   \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse
2444   \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse\fifthargumentfalse
2445   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse\fifthargumentfalse
2446   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumentfalse
2447   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue
2448   \fi
2449   #1[#2][#3][#4][#5][#6]}
2450
2451\tolerant\def\syst_sixtuple_empty#1\relax[#S#+]#*[#S#+]#*[#S#+]#*[#S#+]#*[#S#+]#*[#S#+]%
2452  {\ifarguments
2453   \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse
2454   \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse
2455   \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse
2456   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse\fifthargumentfalse\sixthargumentfalse
2457   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumentfalse\sixthargumentfalse
2458   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \sixthargumentfalse
2459   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \sixthargumenttrue
2460   \fi
2461   #1[#2][#3][#4][#5][#6][#7]}
2462
2463\tolerant\def\syst_seventuple_empty#1\relax[#S#+]#*[#S#+]#*[#S#+]#*[#S#+]#*[#S#+]#*[#S#+]#*[#S#+]%
2464  {\ifarguments
2465   \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse\seventhargumentfalse
2466   \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse\seventhargumentfalse
2467   \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse\seventhargumentfalse
2468   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse\fifthargumentfalse\sixthargumentfalse\seventhargumentfalse
2469   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumentfalse\sixthargumentfalse\seventhargumentfalse
2470   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \sixthargumentfalse\seventhargumentfalse
2471   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \sixthargumenttrue \seventhargumentfalse
2472   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \sixthargumenttrue \seventhargumenttrue
2473   \fi
2474   #1[#2][#3][#4][#5][#6][#7][#8]}
2475
2476\permanent\protected\def\dosingleempty    #1{\syst_single_empty    #1\relax}
2477\permanent\protected\def\dodoubleempty    #1{\syst_double_empty    #1\relax}
2478\permanent\protected\def\dotripleempty    #1{\syst_triple_empty    #1\relax}
2479\permanent\protected\def\doquadrupleempty #1{\syst_quadruple_empty #1\relax}
2480\permanent\protected\def\doquintupleempty #1{\syst_quintuple_empty #1\relax}
2481\permanent\protected\def\dosixtupleempty  #1{\syst_sixtuple_empty  #1\relax}
2482\permanent\protected\def\doseventupleempty#1{\syst_seventuple_empty#1\relax}
2483
2484%D Aliases:
2485
2486\aliased\let\dosingleargument    \dosingleempty
2487\aliased\let\dodoubleargument    \dodoubleempty
2488\aliased\let\dotripleargument    \dotripleempty
2489\aliased\let\doquadrupleargument \doquadrupleempty
2490\aliased\let\doquintupleargument \doquintupleempty
2491\aliased\let\dosixtupleargument  \dosixtupleempty
2492\aliased\let\doseventupleargument\doseventupleempty
2493
2494%D \macros
2495%D   {strippedcsname}
2496%D
2497%D The next macro can be very useful when using \type{\csname} like in:
2498%D
2499%D \starttyping
2500%D \csname if\strippedcsname\something\endcsname
2501%D \stoptyping
2502%D
2503%D This expands to \type{\ifsomething}.
2504
2505\aliased\let\strippedcsname\csstring
2506
2507%D \macros
2508%D   {complexorsimple,complexorsimpleempty}
2509%D
2510%D Setups can be optional. A command expecting a setup is prefixed by \type
2511%D {\complex}, a command without one gets the prefix \type {\simple}. Commands like
2512%D this can be defined by:
2513%D
2514%D \starttyping
2515%D \complexorsimple\command
2516%D \stoptyping
2517%D
2518%D When \type{\command} is followed by a \type{[setup]}, then
2519%D
2520%D \starttyping
2521%D \complexcommand [setup]
2522%D \stoptyping
2523%D
2524%D executes, else we get
2525%D
2526%D \starttyping
2527%D \simplecommand
2528%D \stoptyping
2529%D
2530%D An alternative for \type{\complexorsimple} is:
2531%D
2532%D \starttyping
2533%D \complexorsimpleempty {command}
2534%D \stoptyping
2535%D
2536%D Depending on the presence of \type{[setup]}, this one leads to one of:
2537%D
2538%D \starttyping
2539%D \complexcommando [setup]
2540%D \complexcommando []
2541%D \stoptyping
2542%D
2543%D Many \CONTEXT\ commands started as complex or simple ones, but changed into more
2544%D versatile (more object oriented) ones using the \type {\get..argument} commands.
2545
2546\permanent\protected\def\complexorsimple#1%
2547  {\doifelsenextoptional
2548     {\firstargumenttrue \csname\s!complex\csstring#1\endcsname}
2549     {\firstargumentfalse\csname\s!simple \csstring#1\endcsname}}
2550
2551\permanent\protected\def\complexorsimpleempty#1%
2552  {\doifelsenextoptional
2553     {\firstargumenttrue \csname\s!complex\csstring#1\endcsname}
2554     {\firstargumentfalse\csname\s!complex\csstring#1\endcsname[]}}
2555
2556%D \macros
2557%D   {definecomplexorsimple,definecomplexorsimpleempty}
2558%D
2559%D The previous commands are used that often that we found it worthwile to offer two
2560%D more alternatives. Watch the build in protection.
2561
2562\protected\def\syst_helpers_complex_or_simple#1#2%
2563  {\doifelsenextoptional{\firstargumenttrue#1}{\firstargumentfalse#2}}
2564
2565\protected\def\syst_helpers_complex_or_simple_empty#1%
2566  {\doifelsenextoptional{\firstargumenttrue#1}{\firstargumentfalse#1[]}}
2567
2568\permanent\protected\def\definecomplexorsimple#1%
2569  {\protected\edef#1{\syst_helpers_complex_or_simple
2570     \expandafter\noexpand\csname\s!complex\csstring#1\endcsname
2571     \expandafter\noexpand\csname\s!simple \csstring#1\endcsname}}
2572
2573\permanent\protected\def\definecomplexorsimpleempty#1%
2574  {\protected\edef#1{\syst_helpers_complex_or_simple_empty
2575     \expandafter\noexpand\csname\s!complex\csstring#1\endcsname}}
2576
2577%D These commands are called as:
2578%D
2579%D \starttyping
2580%D \definecomplexorsimple\command
2581%D \stoptyping
2582%D
2583%D Of course, we must have available
2584%D
2585%D \starttyping
2586%D \def\complexcommand[#1]{...}
2587%D \def\simplecommand     {...}
2588%D \stoptyping
2589%D
2590%D Using this construction saves a few string now and then.
2591
2592%D \macros
2593%D   {dosinglegroupempty,dodoublegroupempty,dotriplegroupempty,
2594%D    doquadruplegroupempty, doquintuplegroupempty}
2595%D
2596%D We've already seen some commands that take care of
2597%D optional arguments between \type{[]}. The next two commands
2598%D handle the ones with \type{{}}. They are called as:
2599%D
2600%D \starttyping
2601%D \dosinglegroupempty    \ineedONEargument
2602%D \dodoublegroupempty    \ineedTWOarguments
2603%D \dotriplegroupempty    \ineedTHREEarguments
2604%D \doquadruplegroupempty \ineedFOURarguments
2605%D \doquintuplegroupempty \ineedFIVEarguments
2606%D \stoptyping
2607%D
2608%D We can add additional definitions later when we have defined \type {\appendtoks}.
2609
2610\newconditional\c_syst_helpers_permit_spaces_between_groups
2611
2612\permanent\protected\def    \permitspacesbetweengroups{\c_syst_helpers_permit_spaces_between_groups\conditionaltrue}
2613\permanent\protected\def\dontpermitspacesbetweengroups{\c_syst_helpers_permit_spaces_between_groups\conditionalfalse}
2614
2615\dontpermitspacesbetweengroups
2616
2617%D We can avoid the nasty if handling in \type {syst-gen} by splitting the lot in
2618%D pieces so that we have no nested \type {\nextarguments} potentially being an
2619%D \type {conditional} token. Okay, these macros are not called that often but it
2620%D saves crap when tracing.
2621
2622% \protected\def\dosinglegroupempty#1%
2623%   {\t_syst_aux{#1}%
2624%    \futureexpand\bgroup\syst_helpers_single_empty_one_yes\syst_helpers_single_group_empty_one_nop}
2625%
2626% \def\syst_helpers_single_group_empty_one_nop
2627%   {\firstargumentfalse
2628%    \the\t_syst_aux{}}
2629%
2630% \protected\def\dodoublegroupempty#1%
2631%   {\t_syst_aux{#1}%
2632%    \futureexpand\bgroup\syst_helpers_group_double_empty_one_yes\syst_helpers_group_double_empty_one_nop}
2633%
2634% % \def\syst_helpers_group_double_empty_one_yes#1%
2635% %   {\firstargumenttrue
2636% %    \toksapp\t_syst_aux{{#1}}%
2637% %    \futureexpand\bgroup\syst_helpers_double_empty_two_yes\syst_helpers_group_double_empty_two_nop}
2638% %
2639% % nicer is:
2640%
2641% \def\syst_helpers_group_double_empty_one_yes#+%
2642%   {\firstargumenttrue
2643%    \toksapp\t_syst_aux{#1}%
2644%    \futureexpand\bgroup\syst_helpers_double_empty_two_yes\syst_helpers_group_double_empty_two_nop}
2645%
2646% \def\syst_helpers_group_double_empty_one_nop
2647%   {\firstargumentfalse
2648%    \secondargumentfalse
2649%    \the\t_syst_aux{}{}}
2650%
2651% \def\syst_helpers_group_double_empty_two_nop
2652%   {\secondargumentfalse
2653%    \the\t_syst_aux{}}
2654%
2655% \protected\def\dotriplegroupempty#1%
2656%   {\t_syst_aux{#1}%
2657%    \futureexpand\bgroup\syst_helpers_group_triple_empty_one_yes\syst_helpers_group_triple_empty_one_nop}
2658%
2659% \def\syst_helpers_group_triple_empty_one_yes#+%
2660%   {\firstargumenttrue
2661%    \toksapp\t_syst_aux{#1}%
2662%    \futureexpand\bgroup\syst_helpers_group_triple_empty_two_yes\syst_helpers_group_triple_empty_two_nop}
2663%
2664% \def\syst_helpers_group_triple_empty_two_yes#+%
2665%   {\secondargumenttrue
2666%    \toksapp\t_syst_aux{#1}%
2667%    \futureexpand\bgroup\syst_helpers_triple_empty_three_yes\syst_helpers_group_triple_empty_three_nop}
2668%
2669% \def\syst_helpers_group_triple_empty_one_nop
2670%   {\firstargumentfalse
2671%    \secondargumentfalse
2672%    \thirdargumentfalse
2673%    \the\t_syst_aux{}{}{}}
2674%
2675% \def\syst_helpers_group_triple_empty_two_nop
2676%   {\secondargumentfalse
2677%    \thirdargumentfalse
2678%    \the\t_syst_aux{}{}}
2679%
2680% \def\syst_helpers_group_triple_empty_three_nop
2681%   {\thirdargumentfalse
2682%    \the\t_syst_aux{}}
2683%
2684% \protected\def\doquadruplegroupempty#1%
2685%   {\t_syst_aux{#1}%
2686%    \futureexpand\bgroup\syst_helpers_group_quadruple_empty_one_yes\syst_helpers_group_quadruple_empty_one_nop}
2687%
2688% \def\syst_helpers_group_quadruple_empty_one_yes#+%
2689%   {\firstargumenttrue
2690%    \toksapp\t_syst_aux{#1}%
2691%    \futureexpand\bgroup\syst_helpers_group_quadruple_empty_two_yes\syst_helpers_group_quadruple_empty_two_nop}
2692%
2693% \def\syst_helpers_group_quadruple_empty_two_yes#+%
2694%   {\secondargumenttrue
2695%    \toksapp\t_syst_aux{#1}%
2696%    \futureexpand\bgroup\syst_helpers_group_quadruple_empty_three_yes\syst_helpers_group_quadruple_empty_three_nop}
2697%
2698% \def\syst_helpers_group_quadruple_empty_three_yes#+%
2699%   {\thirdargumenttrue
2700%    \toksapp\t_syst_aux{#1}%
2701%    \futureexpand\bgroup\syst_helpers_quadruple_empty_four_yes\syst_helpers_group_quadruple_empty_four_nop}
2702%
2703% \def\syst_helpers_group_quadruple_empty_one_nop
2704%   {\firstargumentfalse
2705%    \secondargumentfalse
2706%    \thirdargumentfalse
2707%    \fourthargumentfalse
2708%    \the\t_syst_aux{}{}{}{}}
2709%
2710% \def\syst_helpers_group_quadruple_empty_two_nop
2711%   {\secondargumentfalse
2712%    \thirdargumentfalse
2713%    \fourthargumentfalse
2714%    \the\t_syst_aux{}{}{}}
2715%
2716% \def\syst_helpers_group_quadruple_empty_three_nop
2717%   {\thirdargumentfalse
2718%    \fourthargumentfalse
2719%    \the\t_syst_aux{}{}}
2720%
2721% \def\syst_helpers_group_quadruple_empty_four_nop
2722%   {\fourthargumentfalse
2723%    \the\t_syst_aux{}}
2724%
2725% \protected\def\doquintuplegroupempty#1%
2726%   {\t_syst_aux{#1}%
2727%    \futureexpand\bgroup\syst_helpers_group_quintuple_empty_one_yes\syst_helpers_group_quintuple_empty_one_nop}
2728%
2729% \def\syst_helpers_group_quintuple_empty_one_yes#+%
2730%   {\firstargumenttrue
2731%    \toksapp\t_syst_aux{#1}%
2732%    \futureexpand\bgroup\syst_helpers_group_quintuple_empty_two_yes\syst_helpers_group_quintuple_empty_two_nop}
2733%
2734% \def\syst_helpers_group_quintuple_empty_two_yes#+%
2735%   {\secondargumenttrue
2736%    \toksapp\t_syst_aux{#1}%
2737%    \futureexpand\bgroup\syst_helpers_group_quintuple_empty_three_yes\syst_helpers_group_quintuple_empty_three_nop}
2738%
2739% \def\syst_helpers_group_quintuple_empty_three_yes#+%
2740%   {\thirdargumenttrue
2741%    \toksapp\t_syst_aux{#1}%
2742%    \futureexpand\bgroup\syst_helpers_group_quintuple_empty_four_yes\syst_helpers_group_quintuple_empty_four_nop}
2743%
2744% \def\syst_helpers_group_quintuple_empty_four_yes#+%
2745%   {\fourthargumenttrue
2746%    \toksapp\t_syst_aux{#1}%
2747%    \futureexpand\bgroup\syst_helpers_quintuple_empty_five_yes\syst_helpers_group_quintuple_empty_five_nop}
2748%
2749% \def\syst_helpers_group_quintuple_empty_one_nop
2750%   {\firstargumentfalse
2751%    \secondargumentfalse
2752%    \thirdargumentfalse
2753%    \fourthargumentfalse
2754%    \fifthargumentfalse
2755%    \the\t_syst_aux{}{}{}{}{}}
2756%
2757% \def\syst_helpers_group_quintuple_empty_two_nop
2758%   {\secondargumentfalse
2759%    \thirdargumentfalse
2760%    \fourthargumentfalse
2761%    \fifthargumentfalse
2762%    \the\t_syst_aux{}{}{}{}}
2763%
2764% \def\syst_helpers_group_quintuple_empty_three_nop
2765%   {\thirdargumentfalse
2766%    \fourthargumentfalse
2767%    \fifthargumentfalse
2768%    \the\t_syst_aux{}{}{}}
2769%
2770% \def\syst_helpers_group_quintuple_empty_four_nop
2771%   {\fourthargumentfalse
2772%    \fifthargumentfalse
2773%    \the\t_syst_aux{}{}}
2774%
2775% \def\syst_helpers_group_quintuple_empty_five_nop
2776%   {\fifthargumentfalse
2777%    \the\t_syst_aux{}}
2778
2779\tolerant\def\syst_single_group_empty#1\relax#_%
2780  {\ifarguments
2781   \or\firstargumentfalse
2782   \or\firstargumenttrue
2783   \fi
2784   #1#2}
2785
2786\tolerant\def\syst_double_group_empty#1\relax#_#*#_%
2787  {\ifarguments
2788   \or\firstargumentfalse\secondargumentfalse
2789   \or\firstargumenttrue \secondargumentfalse
2790   \or\firstargumenttrue \secondargumenttrue
2791   \fi
2792   #1#2#3}
2793
2794\tolerant\def\syst_triple_group_empty#1\relax#_#*#_#*#_%
2795  {\ifarguments
2796   \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse
2797   \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse
2798   \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse
2799   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue
2800   \fi
2801   #1#2#3#4}
2802
2803\tolerant\def\syst_quadruple_group_empty#1\relax#_#*#_#*#_#*#_%
2804  {\ifarguments
2805   \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse
2806   \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse
2807   \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse
2808   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse
2809   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue
2810   \fi
2811   #1#2#3#4#5}
2812
2813\tolerant\def\syst_quintuple_group_empty#1\relax#_#*#_#*#_#*#_#*#_%
2814  {\ifarguments
2815   \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse
2816   \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse
2817   \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse\fifthargumentfalse
2818   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse\fifthargumentfalse
2819   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumentfalse
2820   \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue
2821   \fi
2822   #1#2#3#4#5#6}
2823
2824\permanent\protected\def\dosinglegroupempty    #1{\syst_single_group_empty    #1\relax}
2825\permanent\protected\def\dodoublegroupempty    #1{\syst_double_group_empty    #1\relax}
2826\permanent\protected\def\dotriplegroupempty    #1{\syst_triple_group_empty    #1\relax}
2827\permanent\protected\def\doquadruplegroupempty #1{\syst_quadruple_group_empty #1\relax}
2828\permanent\protected\def\doquintuplegroupempty #1{\syst_quintuple_group_empty #1\relax}
2829
2830%D These macros can explictly take care of spaces, which means that the next
2831%D definition and calls are valid:
2832%D
2833%D \starttyping
2834%D \def\test#1#2#3{[#1#2#3]}
2835%D
2836%D \dotriplegroupempty\test {a}{b}{c}
2837%D \dotriplegroupempty\test {a}{b}
2838%D \dotriplegroupempty\test {a}
2839%D \dotriplegroupempty\test
2840%D \dotriplegroupempty\test {a} {b} {c}
2841%D \dotriplegroupempty\test {a} {b}
2842%D \dotriplegroupempty\test
2843%D   {a}
2844%D   {b}
2845%D \stoptyping
2846%D
2847%D And alike.
2848
2849%D \macros
2850%D   {firstofoneargument, firstoftwoarguments, firstofthreearguments
2851%D    secondoftwoarguments, secondofthreearguments,
2852%D    thirdofthreearguments}
2853%D
2854%D The next six macros (dedicated to Taco) can conveniently used to select
2855%D arguments. Their names explain their functionality.
2856
2857\permanent\def\firstofoneargument            #1{#1}
2858
2859\permanent\def\firstoftwoarguments         #1#-{#1}
2860\permanent\def\secondoftwoarguments        #-#1{#1}
2861
2862\permanent\def\firstofthreearguments     #1#-#-{#1}
2863\permanent\def\secondofthreearguments    #-#1#-{#1}
2864\permanent\def\thirdofthreearguments     #-#-#1{#1}
2865
2866\permanent\def\firstoffourarguments    #1#-#-#-{#1}
2867\permanent\def\secondoffourarguments   #-#1#-#-{#1}
2868\permanent\def\thirdoffourarguments    #-#-#1#-{#1}
2869\permanent\def\fourthoffourarguments   #-#-#-#1{#1}
2870
2871\permanent\def\firstoffivearguments  #1#-#-#-#-{#1}
2872\permanent\def\secondoffivearguments #-#1#-#-#-{#1}
2873\permanent\def\thirdoffivearguments  #-#-#1#-#-{#1}
2874\permanent\def\fourthoffivearguments #-#-#-#1#-{#1}
2875\permanent\def\fifthoffivearguments  #-#-#-#-#1{#1}
2876
2877\permanent\def\firstofsixarguments #1#-#-#-#-#-{#1}
2878\permanent\def\secondofsixarguments#-#1#-#-#-#-{#1}
2879\permanent\def\thirdofsixarguments #-#-#1#-#-#-{#1}
2880\permanent\def\fourthofsixarguments#-#-#-#1#-#-{#1}
2881\permanent\def\fifthofsixarguments #-#-#-#-#1#-{#1}
2882\permanent\def\sixthofsixarguments #-#-#-#-#-#1{#1}
2883
2884\permanent\protected\def\firstofoneunexpanded       #1{#1}
2885
2886\permanent\protected\def\firstoftwounexpanded     #1#-{#1}
2887\permanent\protected\def\secondoftwounexpanded    #-#1{#1}
2888
2889\permanent\protected\def\firstofthreeunexpanded #1#-#-{#1}
2890\permanent\protected\def\secondofthreeunexpanded#-#1#-{#1}
2891\permanent\protected\def\thirdofthreeunexpanded #-#-#1{#1}
2892
2893%D \macros
2894%D   {globalletempty,letempty,
2895%D    letvalueempty,letgvalueempty,
2896%D    letvaluerelax,letgvaluerelax}
2897%D
2898%D Trivial:
2899
2900\permanent\protected\def\letempty      #1{\lettonothing #1}
2901\permanent\protected\def\globalletempty#1{\glettonothing#1}
2902
2903\permanent\protected\def\letvalueempty #1{\letcsname #1\endcsname\empty}
2904\permanent\protected\def\letgvalueempty#1{\gletcsname#1\endcsname\empty}
2905\permanent\protected\def\letvaluerelax #1{\letcsname #1\endcsname\relax}
2906\permanent\protected\def\letgvalurelax #1{\gletcsname#1\endcsname\relax}
2907
2908\permanent\protected\def\relaxvalueifundefined#1%
2909  {\ifcsname#1\endcsname \else
2910     \aliased\letcsname#1\endcsname\relax
2911   \fi}
2912
2913%D \macros
2914%D   {wait}
2915%D
2916%D The next macro hardly needs explanation. Because no nesting is to be expected, we
2917%D can reuse \type {\wait} within \type {\wait} itself.
2918
2919\permanent\protected\def\wait
2920  {\begingroup
2921   \read16 to \wait
2922   \endgroup}
2923
2924% %D \macros
2925% %D   {writestring,writeline,writestatus}
2926% %D
2927% %D Maybe one didn't notice, but we've already introduced a macro for showing
2928% %D messages. In the multi||lingual modules, we will also introduce a mechanism for
2929% %D message passing. For the moment we stick to the core macros:
2930% %D
2931% %D \starttyping
2932% %D \writestring {string}
2933% %D \writeline
2934% %D \writestatus {category} {message}
2935% %D \stoptyping
2936% %D
2937% %D Messages are formatted. One can provide the maximum with of the identification
2938% %D string with the macro \type {\statuswidth}.
2939%
2940% \setnewconstant\statuswidth  15
2941% \setnewconstant\statuswrite 128 % \pluscxxviii
2942%
2943% \ifdefined\writestring \else
2944%
2945%     \protected\def\writestring{\immediate\write\statuswrite}
2946%     \protected\def\writeline  {\writestring{}}
2947%
2948% \fi
2949%
2950% \protected\def\normalwritestatus#1#2%
2951%   {\writestring{\expandafter\syst_helpers_split_status_yes\expandafter\statuswidth#1%
2952%      \space\space\space\space\space\space\space
2953%      \space\space\space\space\space\space\space
2954%      \space\space\space\space\space\space\end
2955%      \space:\space#2}}
2956%
2957% \def\syst_helpers_split_status_yes#1#2%
2958%   {\ifcase#1 \expandafter\syst_helpers_split_status_nop\fi#2%
2959%    \expandafter\syst_helpers_split_status_yes\expandafter{\the\numexpr#1+\minusone\relax}}
2960%
2961% \def\syst_helpers_split_status_nop#1\end
2962%   {}
2963
2964%D \macros
2965%D   {immediatemessage}
2966%D
2967%D A fully expandable message:
2968
2969% \immediatemessage % {} mandate / defined at the lua end
2970
2971%D \macros
2972%D   {rawgetparameters}
2973%D
2974%D A raw and dirty alternative for \type {\getparameters}; no checking is done!
2975
2976\def\syst_helpers_grab_raw_parameter#1=#2,%
2977  {\ifarguments\or\or
2978     \defcsname\m_syst_parameter_n#1\endcsname{#2}%
2979     \expandafter\syst_helpers_grab_raw_parameter_next
2980   \fi}
2981
2982\def\syst_helpers_grab_raw_parameter_next
2983  {\expandafterspaces\syst_helpers_grab_raw_parameter}
2984
2985\permanent\protected\def\rawgetparameters#1[#2]#*[#3]%
2986  {\def\m_syst_parameter_n{#2}%
2987  %\expandafterspaces\syst_helpers_grab_raw_parameter#3\ignorearguments\ignorearguments}
2988   \expandafter\expandafterspaces\expandafter\syst_helpers_grab_raw_parameter#3\ignorearguments\ignorearguments}
2989
2990%D \macros
2991%D   {doglobal,
2992%D    redoglobal,dodoglobal,resetglobal}
2993%D
2994%D The two macros \type {\redoglobal} and \type{\dodoglobal} are used in this and
2995%D some other modules to enforce a user specified \type {\doglobal} action. The last
2996%D and often only global assignment in a macro is done with \type {\dodoglobal}, but
2997%D all preceding ones with \type {\redoglobal}. When using only alternatives, one
2998%D can reset this mechanism with \type {\resetglobal}.
2999
3000\permanent\protected\def\resetglobal
3001  {\enforced\let\redoglobal\relax
3002   \enforced\let\dodoglobal\relax}
3003
3004\resetglobal
3005
3006\permanent\protected\def\doglobal
3007  {\ifrelax\redoglobal
3008     \enforced\let\redoglobal\global
3009     \enforced\let\dodoglobal\syst_helpers_dodo_global
3010   \fi}
3011
3012\def\syst_helpers_dodo_global
3013  {\resetglobal\global}
3014
3015\let\syst_helpers_redo_global\relax
3016
3017\permanent\protected\def\saveglobal
3018  {\let\syst_helpers_dodo_global\dodoglobal
3019   \let\syst_helpers_redo_global\redoglobal}
3020
3021\permanent\protected\def\restoreglobal
3022  {\enforced\let\redoglobal\syst_helpers_redo_global
3023   \enforced\let\dodoglobal\syst_helpers_dodo_global}
3024
3025%D Whatever (will be overloaded):
3026
3027\protected\def\define#1%
3028  {\ifdefined#1%
3029     \message{[\noexpand#1is already defined]}%
3030     \protected\expandafter\def\expandafter\gobbleddefinition
3031   \else
3032     \protected\expandafter\def
3033   \fi#1}
3034
3035\protected\def\redefine#1%
3036  {\ifdefined#1%
3037     \message{[\noexpand#1is redefined]}%
3038   \fi
3039   \protected\def#1}
3040
3041% \permanent\protected\def\definemacro#1%
3042%   {\ifdefined#1%
3043%      \message{[\noexpand#1is already defined]}%
3044%      \protected\expandafter\def\expandafter\gobbleddefinition
3045%    \else
3046%      \protected\expandafter\def
3047%    \fi#1}
3048
3049% \define\hans{hans}
3050% \redefine\hans{hans}
3051% \define\hans#1[]#2#3{hans}
3052
3053%D The next variant fits nicely in the setups syntax:
3054%D
3055%D \starttyping
3056%D \starttexdefinition bagger [#1] #2
3057%D     oeps
3058%D     #1
3059%D     oeps
3060%D \stoptexdefinition
3061%D
3062%D \bagger [a] {b}
3063%D \stoptyping
3064
3065% \starttexdefinition test
3066% oeps
3067% \stoptexdefinition
3068%
3069% [\test]
3070
3071%D When okay the parameter hack will also go into \MKIV.
3072
3073\integerdef\c_syst_parameter_catcode\parametercatcode
3074
3075% \catcode\endoflineasciicode\activecatcode\letcharcode\endoflineasciicode\par
3076
3077\bgroup
3078
3079\catcode\endoflineasciicode\activecatcode%
3080\letcharcode\endoflineasciicode\par%
3081
3082\permanent\protected\gdef\starttexdefinition%
3083  {\integerdef\c_syst_parameter_catcode\catcode\hashasciicode%
3084   \catcode\hashasciicode\parametercatcode%
3085   \bgroup%
3086   \catcode\endoflineasciicode\activecatcode%
3087   \letcharcode\endoflineasciicode\par%
3088   \syst_helpers_start_tex_definition}
3089
3090\gdef\syst_helpers_start_tex_definition#1
3091  {%catcodetable\ctxcatcodes% can be adapted
3092   \catcode\endoflineasciicode\ignorecatcode%
3093   \clf_texdefinition_one{#1}}
3094
3095\permanent\protected\glettonothing\stoptexdefinition
3096
3097\permanent\gdef\dostarttexdefinition#1\stoptexdefinition%
3098  {\egroup%
3099   \clf_texdefinition_two{#1}%
3100   \catcode\hashasciicode\c_syst_parameter_catcode}
3101
3102\egroup
3103
3104% \protected\def\texdefinition#1{\csname\ifcsname#1\endcsname#1\else donothing\fi\endcsname} % todo: a nop cs: char 0 or some corenamespace
3105
3106\permanent\protected\def\texdefinition#1{\begincsname#1\endcsname}
3107
3108%D \macros
3109%D   {newcounter,
3110%D    increment,decrement}
3111%D
3112%D Unfortunately the number of \COUNTERS\ in \TEX\ is limited, but fortunately we
3113%D can store numbers in a macro. We can increment such pseudo \COUNTERS\ with \type
3114%D {\increment}.
3115%D
3116%D \starttyping
3117%D \increment(\counter,20)
3118%D \increment(\counter,-4)
3119%D \increment(\counter)
3120%D \increment\counter
3121%D \stoptyping
3122%D
3123%D After this sequence of commands, the value of \type {\counter} is 20, 16, 17
3124%D and~18. Of course there is also the complementary command \type {\decrement}.
3125%D
3126%D Global assignments are possible too, using \type{\doglobal}:
3127%D
3128%D \starttyping
3129%D \doglobal\increment\counter
3130%D \stoptyping
3131%D
3132%D When \type {\counter} is undefined, it's value is initialized at~0. It is
3133%D nevertheless better to define a \COUNTER\ explicitly. One reason could be that
3134%D the \COUNTER\ can be part of a test with \type {\ifnum} and this conditional does
3135%D not accept undefined macro's. The \COUNTER\ in our example can for instance be
3136%D defined with:
3137%D
3138%D \starttyping
3139%D \newcounter\counter
3140%D \stoptyping
3141%D
3142%D The command \type {\newcounter} must not be confused with \type {\newcount}! Of
3143%D course this mechanism is much slower than using \TEX's \COUNTERS\ directly. In
3144%D practice \COUNTERS\ (and therefore our pseudo counters too) are seldom the
3145%D bottleneck in the processing of a text. Apart from some other incompatilities we
3146%D want to mention a pitfal when using \type {\ifnum}.
3147%D
3148%D \starttyping
3149%D \ifnum\normalcounter=\pseudocounter \doif \else \doelse \fi
3150%D \ifnum\pseudocounter=\normalcounter \doif \else \doelse \fi
3151%D \stoptyping
3152%D
3153%D In the first test, \TEX\ continues it's search for the second number after
3154%D reading \type {\pseudocounter}, while in the second test, it stops reading after
3155%D having encountered a real one. Tests like the first one therefore can give
3156%D unexpected results, for instance execution of \type {\doif} even if both numbers
3157%D are unequal.
3158
3159\immutable\def\zerocountervalue{0}
3160
3161\permanent\protected\def\newcounter#1%
3162  {\dodoglobal\let#1\zerocountervalue}
3163
3164%D Nowadays we don't mind a few more tokens if we can gain a bit of speed.
3165
3166\def\syst_helpers_do_increment#1{\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifrelax#1\else#1\fi\fi+\plusone \relax}}
3167\def\syst_helpers_do_decrement#1{\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifrelax#1\else#1\fi\fi+\minusone\relax}}
3168
3169\def\syst_helpers_do_do_do_increment#1,#2){\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifrelax#1\else#1\fi\fi+#2\relax}}
3170\def\syst_helpers_do_do_do_decrement#1,#2){\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifrelax#1\else#1\fi\fi-#2\relax}}
3171
3172\def\syst_helpers_do_do_increment(#1%
3173  {\def\m_syst_action_yes{\syst_helpers_do_do_do_increment#1}%
3174   \def\m_syst_action_nop{\syst_helpers_do_do_do_increment#1,\plusone}%
3175   \doifelsenextcharcs,\m_syst_action_yes\m_syst_action_nop}
3176
3177\def\syst_helpers_do_do_decrement(#1%
3178  {\def\m_syst_action_yes{\syst_helpers_do_do_do_decrement#1}%
3179   \def\m_syst_action_nop{\syst_helpers_do_do_do_decrement#1,\plusone}%
3180   \doifelsenextcharcs,\m_syst_action_yes\m_syst_action_nop}
3181
3182\permanent\protected\def\increment{\doifelsenextcharcs(\syst_helpers_do_do_increment\syst_helpers_do_increment}
3183\permanent\protected\def\decrement{\doifelsenextcharcs(\syst_helpers_do_do_decrement\syst_helpers_do_decrement}
3184
3185\permanent\protected\def\fastincrement#1{\dodoglobal\edef#1{\the\numexpr#1+\plusone \relax}}
3186\permanent\protected\def\fastdecrement#1{\dodoglobal\edef#1{\the\numexpr#1+\minusone\relax}}
3187
3188\permanent\protected\def\incrementvalue#1{\expandafter\increment\csname#1\endcsname}
3189\permanent\protected\def\decrementvalue#1{\expandafter\decrement\csname#1\endcsname}
3190
3191%D \macros
3192%D  {newsignal}
3193%D
3194%D When writing advanced macros, we cannot do without signaling. A signal is a small
3195%D (invisible) kern or penalty that signals the next macro that something just
3196%D happened. This macro can take any action depending on the previous signal.
3197%D Signals must be unique and the next macro takes care of that.
3198%D
3199%D \starttyping
3200%D \newsignal\somesignal
3201%D \stoptyping
3202%D
3203%D Signals old dimensions and can be used in skips, kerns and tests like \type
3204%D {\ifdim}.
3205
3206\newdimension\d_syst_maximum_signal % step is about 0.00025pt
3207
3208\permanent\protected\def\newsignal#1%
3209  {\ifdefined#1\else
3210     \advanceby\d_syst_maximum_signal2\scaledpoint % to be save in rounding
3211     \immutable\dimensiondef#1\d_syst_maximum_signal
3212   \fi}
3213
3214%D \macros
3215%D   {strippedcsname}
3216%D
3217%D The next macro can be very useful when using \type {\csname} like in:
3218%D
3219%D \starttyping
3220%D \csname if\strippedcsname\something\endcsname
3221%D \stoptyping
3222
3223\aliased\let\checkedstrippedcsname\csstring
3224
3225%D \macros
3226%D   {savenormalmeaning}
3227%D
3228%D We will use this one in:
3229
3230\permanent\protected\def\savenormalmeaning#1%
3231  {\ifcsname normal\csstring#1\endcsname \else
3232     \aliased\letcsname normal\csstring#1\endcsname#1%
3233   \fi}
3234
3235%D \macros
3236%D   {dorecurse,recurselevel,recursedepth,
3237%D    dostepwiserecurse}
3238%D
3239%D \TEX\ does not offer us powerfull for||loop mechanisms. On the other hand its
3240%D recursion engine is quite unique. We therefore identify the for||looping macros
3241%D by this method. The most simple alternative is the one that only needs a number.
3242%D
3243%D \starttyping
3244%D \dorecurse {n} {whatever we want}
3245%D \stoptyping
3246%D
3247%D This macro can be nested without problems and therefore be used in situations
3248%D where \PLAIN\ \TEX's \type {\loop} macro ungracefully fails. The current value of
3249%D the counter is available in \type {\recurselevel}, before as well as after the
3250%D \typ {whatever we wat} stuff.
3251%D
3252%D \starttyping
3253%D \dorecurse               % inner loop
3254%D   {10}
3255%D   {\recurselevel:          % outer value
3256%D      \dorecurse          % inner loop
3257%D        {\recurselevel}     % outer value
3258%D        {\recurselevel}     % inner value
3259%D      \dorecurse          % inner loop
3260%D        {\recurselevel}     % outer value
3261%D        {\recurselevel}     % inner value
3262%D    \endgraf}
3263%D \stoptyping
3264%D
3265%D In this example the first, second and fourth \type {\recurselevel} concern the
3266%D outer loop, while the third and fifth one concern the inner loop. The depth of
3267%D the nesting is available for inspection in \type {\recursedepth}.
3268%D
3269%D Both \type {\recurselevel} and \type {\recursedepth} are macros. The real
3270%D \COUNTERS\ are hidden from the user because we don't want any interference.
3271%D
3272%D We now use the macro stack which is somewhat leaner and meaner and a little
3273%D faster too.
3274
3275\newinteger\outerrecurse
3276\newinteger\innerrecurse
3277
3278% \mutable\def\recursedepth{\the\outerrecurse}
3279% \mutable\def\recurselevel{0}
3280
3281\mutable\untraced\def\recursedepth{\the\outerrecurse}
3282\mutable\untraced\def\recurselevel{0}
3283
3284\mutable\let\recurseaction\relax
3285
3286\mutable\lettonothing\recursestring
3287
3288%% \protected\def\syst_helpers_stepwise_recurse#1#2#3% from to step
3289%%   {\ifnum#1>#2\relax
3290%%      \expandafter\gobblefourarguments
3291%%    \else
3292%%      \def\recurselevel{#1}%
3293%%      \doubleexpandafter\syst_helpers_stepwise_recurse_yes
3294%%    \fi\expandafter{\the\numexpr\recurselevel+#3\relax}{#2}{#3}}
3295%%
3296%% \protected\def\syst_helpers_stepwise_recurse_yes
3297%%   {\syst_helpers_recurse_content
3298%%    \syst_helpers_stepwise_recurse}
3299%%
3300%% \protected\def\syst_helpers_stepwise_reverse#1#2#3% from to step
3301%%   {\ifnum#1<#2\relax
3302%%      \expandafter\gobblefourarguments
3303%%    \else
3304%%      \def\recurselevel{#1}%
3305%%      \innerrecurse#1\relax
3306%%      \advanceby\innerrecurse#3\relax
3307%%      \doubleexpandafter\syst_helpers_stepwise_reverse_yes
3308%%    \fi\expandafter{\the\numexpr\recurselevel+#3\relax}{#2}{#3}}
3309%%
3310%% \protected\def\syst_helpers_stepwise_reverse_yes
3311%%   {\syst_helpers_recurse_content
3312%%    \syst_helpers_stepwise_reverse}
3313%%
3314%% \permanent\def\doexpandedrecurse#1#2% user macro (also was \doxprecurse)
3315%%   {\ifnum#1>\zerocount
3316%%      #2\expandafter\doexpandedrecurse\expandafter{\the\numexpr#1-\plusone\relax}{#2}%
3317%%    \fi}
3318
3319%D The next one might replace the above and provides the current step in \type {#1}
3320%D like \type {\dorecurse} do, but it comes with a tiny performance hit.
3321
3322%% \installsystemnamespace{expandedrecurse}
3323%%
3324%% \mutable\let\m_expanded_recursed\gobbleoneargument
3325%%
3326%% \permanent\def\doexpandedrecursed#1#2% this might replace: \doexpandedrecurse
3327%%   {\beginlocalcontrol
3328%%    \localpushmacro\m_expanded_recursed
3329%%    \def\m_expanded_recursed##1{#2}%
3330%%    \endlocalcontrol
3331%%    \syst_do_expanded_recursed{1}{#1}{#2}% no \plusone !
3332%%    \beginlocalcontrol
3333%%    \localpopmacro\m_expanded_recursed
3334%%    \endlocalcontrol}
3335%%
3336%% \def\syst_do_expanded_recursed#1#2#3%
3337%%   {\ifnum#1>#2\norelax
3338%%      \expandafter\gobblethreearguments
3339%%    \else
3340%%      \m_expanded_recursed{#1}\doubleexpandafter\syst_do_expanded_recursed
3341%%    \fi
3342%%    {\the\numexpr#1+\plusone\relax}{#2}{#3}}
3343
3344%D As we can see here, the simple command \type{\dorecurse} is a special case of the
3345%D more general:
3346%D
3347%D \starttyping
3348%D \dostepwiserecurse {from} {to} {step} {action}
3349%D \stoptyping
3350%D
3351%D This commands accepts positive and negative steps. Illegal values are handles as
3352%D good as possible and the macro accepts numbers and \COUNTERS.
3353%D
3354%D \starttyping
3355%D \dostepwiserecurse  {1} {10}  {2} {...}
3356%D \dostepwiserecurse {10}  {1} {-2} {...}
3357%D \stoptyping
3358%D
3359%D Because the simple case (n=1) is used often, we implement it more efficiently:
3360
3361%% \permanent\protected\def\dorecurse#1%
3362%%   {\ifcase#1\relax
3363%%      \expandafter\gobbletwoarguments
3364%%    \or
3365%%      \expandafter\syst_helpers_recurse_y
3366%%    \else
3367%%      \expandafter\syst_helpers_recurse_x
3368%%    \fi{#1}}
3369%%
3370%% \protected\def\syst_helpers_recurse_indeed#1#2% from to
3371%% % {\ifnum#1>#2 %
3372%%   {\ifnum#1>#2\relax
3373%%      \expandafter\syst_helpers_recurse_indeed_nop
3374%%    \else
3375%%      \def\recurselevel{#1}%
3376%%      \innerrecurse#1\advanceby\innerrecurse\plusone
3377%%      \doubleexpandafter\syst_helpers_recurse_indeed_yes
3378%%    \fi\expandafter{\the\innerrecurse}{#2}}
3379%%
3380%% \protected\def\syst_helpers_recurse_indeed_yes
3381%%   {\syst_helpers_recurse_content
3382%%    \syst_helpers_recurse_indeed}
3383%%
3384%% \protected\def\syst_helpers_recurse_indeed_nop#0#0#0%
3385%%   {}
3386
3387%D \macros
3388%D   {dowith}
3389%D
3390%D Here's a loop over whatever is in a list:
3391%D
3392%D \starttyping
3393%D \dowith{a,b,c}{[#1]}
3394%D \stoptyping
3395
3396\let\syst_helpers_with\gobbleoneargument
3397
3398\permanent\protected\def\dowith#1#2%
3399  {\def\syst_helpers_with##1{#2}%
3400   \normalexpanded{\processcommalist[#1]}\syst_helpers_with}
3401
3402%D \macros
3403%D   {doloop,exitloop}
3404%D
3405%D Sometimes loops are not determined by counters, but by (a combinations of)
3406%D conditions. We therefore implement a straightforward loop, which can only be left
3407%D when we explictly exit it. Nesting is supported. First we present a more
3408%D extensive alternative.
3409%D
3410%D \starttyping
3411%D \doloop
3412%D   {Some kind of typesetting punishment \par
3413%D    \ifnum\pageno>100 \exitloop \fi}
3414%D \stoptyping
3415%D
3416%D When needed, one can call for \type {\looplevel} and \type {\loopdepth}.
3417
3418% \aliased\let\endofloop\donothing % maybe \syst_helpers_loop_end
3419%
3420% \permanent\protected\def\doloop#1%
3421%   {\global\advanceby\outerrecurse \plusone
3422%    \push_macro_recurseaction
3423%    \push_macro_recurselevel
3424%    \protected\gdef\recurseaction##1##2{#1}%
3425%    \enforced\let\endofloop\syst_helpers_loop
3426%    \syst_helpers_loop1}% no \plusone else \recurselevel wrong
3427%
3428% \protected\def\syst_helpers_loop#1%
3429%   {\def\recurselevel{#1}%
3430%    \expandafter\syst_helpers_loop_yes\expandafter{\the\numexpr\recurselevel+\plusone\relax}}
3431%
3432% %% \protected\def\syst_helpers_loop_yes
3433% %%   {\syst_helpers_recurse_content
3434% %%    \endofloop}
3435%
3436% \protected\def\syst_helpers_loop_yes
3437%   {\normalexpanded{\recurseaction{\recurselevel}{\the\outerrecurse}}%
3438%    \endofloop}
3439%
3440% \protected\def\syst_helpers_loop_nop#-%
3441%   {\enforced\let\endofloop\syst_helpers_loop
3442%    \pop_macro_recurselevel
3443%    \pop_macro_recurseaction
3444%    \global\advanceby\outerrecurse\minusone}
3445%
3446% \permanent\protected\def\exitloop                % \exitloop quits at end
3447%   {\enforced\let\endofloop\syst_helpers_loop_nop}
3448%
3449% \permanent\protected\def\exitloopnow#-\endofloop % \exitloopnow quits directly
3450%   {\syst_helpers_loop_nop}
3451
3452\permanent\protected\def\doloop#1%
3453  {\push_macro_recurseaction
3454   \push_macro_recurselevel
3455   \push_macro_recursedepth
3456   \protected\gdef\recurseaction##1##2{#1}%
3457   \untraced\edef\recurselevel{\noexpand\the\nestedloopiterator\the\currentloopnesting\space}%
3458   \untraced\edef\recursedepth{\the\numexpr\currentloopnesting+\minusone}%
3459   \localcontrolledendless
3460     {%\edef\recurselevel{\the\currentloopiterator}%
3461      %\edef\recursedepth{\the\currentloopnesting}%
3462      \normalexpanded{\recurseaction{\the\currentloopiterator}{\the\currentloopnesting}}}%
3463   \pop_macro_recursedepth
3464   \pop_macro_recurselevel
3465   \pop_macro_recurseaction}
3466
3467\aliased\let\exitloop   \quitloop
3468\aliased\let\exitloopnow\quitloopnow
3469
3470%D The loop is executed at least once, so beware of situations like:
3471%D
3472%D \starttyping
3473%D \doloop {\exitloop some commands}
3474%D \stoptyping
3475%D
3476%D It's just a matter of putting the text into the \type {\if} statement that should
3477%D be there anyway, like in:
3478%D
3479%D \starttyping
3480%D \doloop {\ifwhatever \exitloop \else some commands\fi}
3481%D \stoptyping
3482%D
3483%D You can also quit a loop immediately, by using \type
3484%D {\exitloopnow} instead. Beware, this is more sensitive
3485%D for conditional errors.
3486
3487%D Krzysztof Leszczynski suggested to provide access to the level by means of a
3488%D \type {#1}. I decided to pass the more frequently used level as \type {#1} and
3489%D the less favoured depth as \type {#2}. The intended usage is:
3490%D
3491%D \starttyping
3492%D \dorecurse{3}{\definesymbol[test-#1][xx-#1]}
3493%D
3494%D \def\test{\dorecurse{3}{\definesymbol[test-##1][xx-##1]}} \test
3495%D
3496%D \symbol[test-1]\quad\symbol[test-2]\quad\symbol[test-3]
3497%D \stoptyping
3498%D
3499%D Since the hashed arguments are expanded, we don't need tricky expansion here.
3500%D
3501%D \starttyping
3502%D \dorecurse{3}{\expanded{\definesymbol[test-\recurselevel][xx-\recurselevel]}}
3503%D \stoptyping
3504
3505%% \def\syst_helpers_recurse_content
3506%%   {\normalexpanded{\recurseaction{\recurselevel}{\the\outerrecurse}}}
3507%%
3508%% \protected\def\syst_helpers_recurse_x#1#2%
3509%%   {\global\advanceby\outerrecurse\plusone
3510%%    \globalpushmacro\recurseaction
3511%%    \globalpushmacro\recurselevel
3512%%    \protected\gdef\recurseaction##1##2{#2}%
3513%%    \expandafter\syst_helpers_recurse_indeed\expandafter1\expandafter{\number#1}%
3514%%    \globalpopmacro\recurselevel
3515%%    \globalpopmacro\recurseaction
3516%%    \global\advanceby\outerrecurse\minusone}
3517%%
3518%% \protected\def\syst_helpers_recurse_y#1#2%
3519%%   {\global\advanceby\outerrecurse\plusone
3520%%    \globalpushmacro\recurseaction
3521%%    \globalpushmacro\recurselevel
3522%%    \let\recurselevel\!!plusone
3523%%    \protected\gdef\recurseaction##1##2{#2}%
3524%%    \syst_helpers_recurse_content
3525%%    \globalpopmacro\recurselevel
3526%%    \globalpopmacro\recurseaction
3527%%    \global\advanceby\outerrecurse\minusone}
3528%%
3529%% \permanent\protected\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4 ... todo: remove unused helpers
3530%%   {\global\advanceby\outerrecurse \plusone
3531%%    \globalpushmacro\recurseaction
3532%%    \globalpushmacro\recurselevel
3533%%    \protected\gdef\recurseaction##1##2{#4}%
3534%%    \normalexpanded{\ifcmpnum#3\zerocount
3535%%      \ifnum#1<#2\relax\relax % so we catch \the\numexpr xx without \relax's
3536%%        \doubleexpandafter\gobbletwoarguments
3537%%      \else
3538%%        \tripleexpandafter\syst_helpers_stepwise_reverse
3539%%      \fi
3540%%    \or
3541%%      \doubleexpandafter\gobbletwoarguments
3542%%    \orelse\ifnum#2<#1\relax\relax % so we catch \the\numexpr xx without \relax's
3543%%      \doubleexpandafter\gobbletwoarguments
3544%%    \else
3545%%      \doubleexpandafter\syst_helpers_stepwise_recurse
3546%%    \fi\normalexpanded{{\number#1}{\number#2}{\number#3}}}%
3547%%    \globalpopmacro\recurselevel
3548%%    \globalpopmacro\recurseaction
3549%%    \global\advanceby\outerrecurse\minusone}
3550
3551\newinteger\fastloopindex
3552\newinteger\fastloopfinal
3553
3554\let\m_syst_helpers_fast_loop_cs\relax
3555
3556\permanent\protected\def\dofastloopcs#1%
3557  {\fastloopfinal#1\relax
3558   \ifcase\fastloopfinal
3559     \expandafter\gobbleoneargument
3560   \else
3561     \expandafter\syst_helpers_fast_loop_cs
3562   \fi}
3563
3564\protected\def\syst_helpers_fast_loop_cs#1%
3565  {\let\m_syst_helpers_fast_loop_cs#1%
3566   \fastloopindex\plusone
3567   \syst_helpers_fast_loop_cs_step}
3568
3569\protected\def\syst_helpers_fast_loop_cs_step
3570  {\ifnum\fastloopindex>\fastloopfinal
3571     \let\m_syst_helpers_fast_loop_cs\relax
3572   \else
3573     \m_syst_helpers_fast_loop_cs
3574     \advanceby\fastloopindex\plusone
3575     \expandafter\syst_helpers_fast_loop_cs_step
3576   \fi}
3577
3578%D Here are the more modern implementations:
3579
3580\permanent\protected\def\installmacrostack#1%
3581  {\ifdefined#1\else\mutable\lettonothing#1\fi
3582   \protected\gdefcsname push_macro_\csstring#1\endcsname{\localpushmacro#1}%
3583   \protected\gdefcsname  pop_macro_\csstring#1\endcsname{\localpopmacro #1}}
3584
3585\permanent\protected\def\installglobalmacrostack#1%
3586  {\ifdefined#1\else\mutable\glettonothing#1\fi
3587   \protected\gdefcsname push_macro_\csstring#1\endcsname{\globalpushmacro#1}%
3588   \protected\gdefcsname  pop_macro_\csstring#1\endcsname{\globalpopmacro #1}}
3589
3590\installglobalmacrostack \recurseaction
3591\installglobalmacrostack \recurselevel
3592\installglobalmacrostack \recursedepth
3593\installglobalmacrostack \recursestring
3594
3595% \showmacrostack can be used to see if there are different entries
3596
3597\permanent\protected\def\dostepwiserecurse#1#2#3#4%
3598  {\push_macro_recurseaction
3599   \push_macro_recurselevel
3600   \push_macro_recursedepth
3601   \protected\gdef\recurseaction##1##2{#4}%
3602   \untraced\edef\recurselevel{\noexpand\the\nestedloopiterator\the\currentloopnesting\space}%
3603   \untraced\edef\recursedepth{\the\numexpr\currentloopnesting+\minusone}%
3604   \localcontrolledloop=#1=#2=#3%
3605     {%\edef\recurselevel{\the\currentloopiterator}%
3606      %\edef\recursedepth{\the\currentloopnesting}%
3607      \normalexpanded{\recurseaction{\the\currentloopiterator}{\the\currentloopnesting}}}%
3608   \pop_macro_recursedepth
3609   \pop_macro_recurselevel
3610   \pop_macro_recurseaction}
3611
3612\permanent\protected\def\dorecurse#1#2%
3613  {\push_macro_recurseaction
3614   \push_macro_recurselevel
3615   \push_macro_recursedepth
3616   \protected\gdef\recurseaction##1##2{#2}%
3617   \untraced\edef\recurselevel{\noexpand\the\nestedloopiterator\the\currentloopnesting\space}%
3618   \untraced\edef\recursedepth{\the\numexpr\currentloopnesting+\minusone}%
3619   \localcontrolledrepeat=#1%
3620     {%\edef\recurselevel{\the\currentloopiterator}%
3621      %\edef\recursedepth{\the\currentloopnesting}%
3622      \normalexpanded{\recurseaction{\the\currentloopiterator}{\the\currentloopnesting}}}%
3623   \pop_macro_recursedepth
3624   \pop_macro_recurselevel
3625   \pop_macro_recurseaction}
3626
3627\permanent\def\doexpandedrecurse#1#2% user macro (also was \doxprecurse)
3628  {\expandedloop\plusone#1\plusone{#2}}
3629
3630\mutable\let\m_expanded_recursed\gobbleoneargument
3631
3632\installmacrostack\m_expanded_recursed
3633
3634\permanent\def\doexpandedrecursed#1#2% this might replace: \doexpandedrecurse
3635  {\beginlocalcontrol
3636   \push_macro_m_expanded_recursed
3637   \def\m_expanded_recursed##1{#2}%
3638   \endlocalcontrol
3639   \expandedrepeat#1{\expandafter\m_expanded_recursed\expandafter{\the\currentloopiterator}}%
3640   \beginlocalcontrol
3641   \pop_macro_m_expanded_recursed
3642   \endlocalcontrol}
3643
3644% Helper:
3645
3646% \permanent\protected\def\resetrecurselevel{\let\recurselevel\!!zerocount}
3647
3648\permanent\protected\def\resetrecurselevel{\untraced\let\recurselevel\!!zerocount}
3649
3650% \appendtoks \resetrecurselevel \to \everydump
3651
3652%D \macros
3653%D   {doloopoverlist}
3654%D
3655%D \starttyping
3656%D \doloopoverlist {red,green,blue} {
3657%D      \setuppalet[\recursestring]
3658%D      \doloopoverlist {light,normal,dark} {
3659%D          \blackrule[color=\recursestring,width=20cm,height=2cm,depth=0cm]\par
3660%D      }
3661%D  }
3662%D \stoptyping
3663%D
3664%D or:
3665%D
3666%D \starttyping
3667%D \doloopoverlist {red,green,blue} {
3668%D      \setuppalet[#1]
3669%D      \doloopoverlist {light,normal,dark} {
3670%D          \blackrule[color=##1,width=20cm,height=2cm,depth=0cm]\par
3671%D      }
3672%D  }
3673%D \stoptyping
3674
3675\permanent\protected\def\doloopoverlist#1#2%
3676  {\global\advanceby\outerrecurse\plusone
3677%    \globalpushmacro\recurseaction
3678%    \globalpushmacro\recursestring
3679   \push_macro_recurseaction
3680   \push_macro_recursestring
3681   \protected\gdef\recurseaction##1{\edef\recursestring{##1}#2}%
3682   \processcommacommand[#1]\recurseaction
3683%    \globalpopmacro\recursestring
3684%    \globalpopmacro\recurseaction
3685   \pop_macro_recursestring
3686   \pop_macro_recurseaction
3687   \global\advanceby\outerrecurse\minusone}
3688
3689%D This is some \LMTX\ experiment:
3690%D
3691%D Think of:
3692%D
3693%D \starttyping
3694%D \doloopovermatch {(\letterpercent w+) *(\letterpercent w*)} {aa bb cc dd} {
3695%D     [
3696%D     \doloopovermatch{(\letterpercent w)(\letterpercent w)} {#1} {(##1 ##2)}
3697%D     \doloopovermatch{(\letterpercent w)(\letterpercent w)} {#2} {(##1 ##2)}
3698%D     ]
3699%D }
3700%D
3701%D and:
3702%D
3703%D \stoptyping
3704%D
3705%D \starttyping
3706%D \def\MyMacro#1{(#1)} \ctxluamatch \MyMacro {(.)} {abcd}
3707%D \stoptyping
3708
3709\aliased\let\matchloopcommand\relax
3710
3711\permanent\protected\def\doloopovermatch#1#2#3%
3712  {\pushmacro\matchloopcommand
3713   \enforced\def\matchloopcommand##1##2##3##4##5##6##7##8##9##A##B##C##D##E{#3}%
3714   \ctxluamatch\matchloopcommand{#1}{#2}%
3715   \popmacro\matchloopcommand}
3716
3717\permanent\def\doloopovermatched#1#2#3%
3718  {\beginlocalcontrol
3719     \pushmacro\matchloopcommand
3720     \enforced\def\matchloopcommand##1##2##3##4##5##6##7##8##9##A##B##C##D##E{#3}%
3721   \endlocalcontrol
3722   \the\ctxluamatch\matchloopcommand{#1}{#2}%
3723   \beginlocalcontrol
3724     \popmacro\matchloopcommand
3725   \endlocalcontrol}
3726
3727% \defineatluaend\ctxluamatch
3728% \defineatluaend\ctxluamatchfile
3729
3730%D \starttyping
3731%D \ctxluamatch
3732%D     \Whatever
3733%D     {(\letterpercent S+) *(\letterpercent S+) *(\letterpercent S+) *(\letterpercent S+)\letterpercent s*}
3734%D     {\cldloadfile{crap.txt}}
3735%D \ctxluamatchfile
3736%D     \Whatever
3737%D     {(\letterpercent S+) *(\letterpercent S+) *(\letterpercent S+) *(\letterpercent S+)\letterpercent s*}
3738%D     {crap.txt}
3739%D \stoptyping
3740
3741%D \macros
3742%D   {newevery,everyline,EveryLine,EveryPar}
3743%D
3744%D Lets skip to something quite different. It's common use to use \type {\everypar}
3745%D for special purposes. In \CONTEXT\ we use this primitive for locating sidefloats.
3746%D This means that when user assignments to \type {\everypar} can interfere with
3747%D those of the package. We therefore introduce \type {\EveryPar}.
3748%D
3749%D The same goes for \type {\EveryLine}. Because \TEX\ offers no \type {\everyline}
3750%D primitive, we have to call for \type {\everyline} when we are working on a line
3751%D by line basis. Just by calling \type {\EveryPar{}} and \type {\EveryLine{}} we
3752%D restore the old situation.
3753
3754% \dorecurse{2}{
3755%     \expanded{\everypar{before \recurselevel\space}}
3756%     \EveryPar{x } [before \recurselevel\space x] \par
3757%     \EveryPar{y } [before \recurselevel\space y] \par
3758%     \EveryPar{}   [before \recurselevel]   \par
3759%     \EveryPar{x } \EveryPar{y } \EveryPar{} [before \recurselevel] \par
3760%     \EveryPar{y } \everypar{before } [before] \par
3761% }
3762
3763\installsystemnamespace{extraevery}
3764
3765\permanent\protected\def\newevery#1#2%
3766  {\ifx#1\everypar\else\newtoks#1\fi% we test for redefinition elsewhere
3767   \ifrelax#2\orelse\ifdefined#2\else
3768     \expandafter\newtoks\csname\??extraevery\csstring#1\endcsname
3769     \frozen\protected\edef#2{\syst_helpers_every#1\csname\??extraevery\csstring#1\endcsname}%
3770   \fi}
3771
3772\protected\def\syst_helpers_every#1#2%
3773  {\removetoks\the#2\from#1%
3774   \appendtoks\the#2\to  #1%
3775   #2}
3776
3777%D This one permits definitions like:
3778
3779\newevery \everypar  \EveryPar  % we get a warning which is ok
3780\newevery \everyline \EveryLine
3781
3782%D and how about:
3783
3784\newtoks \neverypar
3785
3786\permanent\protected\def\forgeteverypar
3787  {\everypar{\expand\neverypar}}
3788
3789%D Which we're going to use indeed! When the second argument equals \type {\relax},
3790%D the first token list is created unless it is already defined.
3791%D
3792%D Technically spoken we could have used the method we are going to present in the
3793%D visual debugger. First we save the primitive \type{\everypar}:
3794%D
3795%D \starttyping
3796%D \let\normaleverypar=\everypar
3797%D \stoptyping
3798%D
3799%D Next we allocate a \TOKENLIST\ named \type{\everypar}, which means that
3800%D \type{\everypar} is no longer a primitive but something like \type{\toks44}.
3801%D
3802%D \starttyping
3803%D \newtoks\everypar
3804%D \stoptyping
3805%D
3806%D Because \TEX\ now executes \type{\normaleverypar} instead of \type{\everypar}, we
3807%D are ready to assign some tokens to this internally known and used \TOKENLIST.
3808%D
3809%D \starttyping
3810%D \normaleverypar={all the things the system wants to do \the\everypar}
3811%D \stoptyping
3812%D
3813%D Where the user can provide his own tokens to be expanded every time he expects
3814%D them to expand.
3815%D
3816%D \starttyping
3817%D \everypar={something the user wants to do}
3818%D \stoptyping
3819%D
3820%D We don't use this method because it undoubtly leads to confusing situations,
3821%D especially when other packages are used, but it's this kind of tricks that make
3822%D \TEX\ so powerful.
3823
3824%D \macros
3825%D   {convertargument,convertcommand,convertvalue}
3826%D
3827%D Some persistent experimenting led us to the next macro. This macro converts a
3828%D parameter or an expanded macro to it's textual meaning.
3829%D
3830%D \starttyping
3831%D \convertargument ... \to \command
3832%D \stoptyping
3833%D
3834%D For example,
3835%D
3836%D \starttyping
3837%D \convertargument{one \two \three{four}}\to\ascii
3838%D \stoptyping
3839%D
3840%D The resulting macro \type{\ascii} can be written to a file or the terminal
3841%D without problems. In \CONTEXT\ we use this macro for generating registers and
3842%D tables of contents.
3843%D
3844%D The second conversion alternative accepts a command:
3845%D
3846%D \starttyping
3847%D \convertcommand\command\to\ascii
3848%D \stoptyping
3849%D
3850%D Both commands accept the prefix \type{\doglobal} for global assignments.
3851
3852\permanent\protected\def\convertvalue#1\to
3853  {\expandafter\convertcommand\csname#1\endcsname\to}
3854
3855\permanent\protected\def\defconvertedvalue#1#2% less sensitive for \to
3856  {\expandafter\defconvertedcommand\expandafter#1\csname#2\endcsname}
3857
3858%D \macros
3859%D   {doifassignmentelse}
3860%D
3861%D A lot of \CONTEXT\ commands take optional arguments, for instance:
3862%D
3863%D \starttyping
3864%D \dothisorthat[alfa,beta]
3865%D \dothisorthat[first=foo,second=bar]
3866%D \dothisorthat[alfa,beta][first=foo,second=bar]
3867%D \stoptyping
3868%D
3869%D Although a combined solution is possible, we prefer a seperation. The next
3870%D command takes care of propper handling of such multi||faced commands.
3871%D
3872%D \starttyping
3873%D \doifassignmentelse {...} {then ...} {else ...}
3874%D \stoptyping
3875%D
3876%D So
3877%D \startbuffer
3878%D \doifelseassignment{a=b}{Y}{N}
3879%D \doifelseassignment{a+b}{Y}{N}
3880%D
3881%D \ifcondition\validassignment  {a=b}Y\else N\fi
3882%D \ifcondition\novalidassignment{a=b}N\else Y\fi
3883%D \ifcondition\novalidassignment {ab}Y\else N\fi
3884%D \stopbuffer
3885%D
3886%D \typebuffer gives: \blank \getbuffer \blank
3887
3888\permanent\protected\def\doifelseassignment#1%
3889  {\ifhastok={#1}%
3890     \expandafter\firstoftwoarguments
3891   \else
3892     \expandafter\secondoftwoarguments
3893   \fi}
3894
3895\permanent\protected\def\doifelseassignmentcs#1#2#3%
3896  {\ifhastok={#1}%
3897     \expandafter#2%
3898   \else
3899     \expandafter#3%
3900   \fi}
3901
3902\aliased\let\doifassignmentelse  \doifelseassignment
3903\aliased\let\doifassignmentelsecs\doifelseassignmentcs
3904
3905\newif\ifassignment
3906
3907\permanent\protected\def\docheckassignment#1%
3908  {\ifhastok={#1}%
3909     \assignmenttrue
3910   \else
3911     \assignmentfalse
3912   \fi}
3913
3914\permanent\protected\def\validassignment  #1{\ifhastok={#1}} % can become: {\ifhastok=} as we enforce {}
3915\permanent\protected\def\novalidassignment#1{\ifnum\ifhastok={#1}\zerocount\else\plusone\fi=\plusone} % or use unless
3916
3917%D In \ETEX\ we can use \type {\detokenize} and gain some speed, but in general far
3918%D less that 1\% for \type {\convertargument} and nil for \type {\convertcommand}.
3919%D This macro is more robust than the pure \TEX\ one, something I found out when
3920%D primitives like \type {\jobname} were fed (or something undefined).
3921
3922\permanent\protected\def\convertargument#1\to#2{\dodoglobal\edef#2{\detokenize{#1}}}
3923\permanent\protected\def\convertcommand #1\to#2{\dodoglobal\edef#2{\expandafter\detokenize\expandafter{#1}}} % hm, only second is also ok
3924
3925\permanent\protected\def\defconvertedargument #1#2{\edef#1{\detokenize{#2}}}
3926\permanent\protected\def\defconvertedcommand  #1#2{\edef#1{\detokenize\expandafter{#2}}}
3927\permanent\protected\def\edefconvertedargument#1#2{\edef#1{#2}%
3928                                                   \edef#1{\detokenize\expandafter{#1}}}
3929\permanent\protected\def\gdefconvertedargument#1#2{\xdef#1{\detokenize{#2}}}
3930\permanent\protected\def\gdefconvertedcommand #1#2{\xdef#1{\detokenize\expandafter{#2}}}
3931\permanent\protected\def\xdefconvertedargument#1#2{\xdef#1{#2}%
3932                                                   \xdef#1{\detokenize\expandafter{#1}}}
3933
3934%D When you try to convert a primitive command, you'll find out that the \ETEX\
3935%D method fails on for instance \type {\jobname} in the sense that it returns the
3936%D filename instead of just \type {\jobname}. So far this does not give real
3937%D problems.
3938
3939%D This is typically a macro that one comes to after reading the \TEX book
3940%D carefully. Even then, the definite solution was found after rereading the \TEX
3941%D book. The first implementation was:
3942%D
3943%D \starttyping
3944%D \def\doconvertargument#1->#2\\\\{#2}
3945%D \stoptyping
3946%D
3947%D The \type {-}, the delimiter \type {\\\\} and the the second argument are
3948%D completely redundant.
3949
3950%D \macros
3951%D   {showvalue}
3952%D
3953%D Ahandy macro, for testing purposes only:
3954
3955\permanent\protected\def\showvalue#1%
3956  {\ifcsname#1\endcsname
3957     \expandafter\show\csname#1\endcsname
3958   \else
3959     \show\undefined
3960   \fi}
3961
3962%D \macros
3963%D   {doifmeaningelse}
3964%D
3965%D We can use both commands in testing, but alas, not all meanings expand to
3966%D something \type {->}. This is no problem in the \ETEX\ implementation, but since
3967%D we want compatibility, we need:
3968%D
3969%D \starttyping
3970%D \doifmeaningelse {\next} {\something} {true} {false}
3971%D \stoptyping
3972%D
3973%D Watch the one level expansion of the second argument.
3974
3975\permanent\protected\def\doifelsemeaning#1#2%
3976  {\edef\m_syst_string_one{\normalmeaning#1}%
3977   \def \m_syst_string_two{#2}%
3978   \edef\m_syst_string_two{\normalmeaning\m_syst_string_two}%
3979   \ifx\m_syst_string_one\m_syst_string_two
3980     \expandafter\firstoftwoarguments
3981   \else
3982     \expandafter\secondoftwoarguments
3983   \fi}
3984
3985\aliased\let\doifmeaningelse\doifelsemeaning
3986
3987%D \macros
3988%D   {doifsamestringselse,doifsamestring,doifnotsamestring}
3989%D
3990%D The next comparison macro converts the arguments into expanded strings. This
3991%D command can be used to compare for instance \type {\jobname} with a name stored
3992%D in a macro.
3993%D
3994%D \starttyping
3995%D \doifelse          {\jobname}{oeps}{YES}{NO}
3996%D \doifsamestringelse{\jobname}{oeps}{YES}{NO}
3997%D \stoptyping
3998
3999\def\syst_helpers_if_samestring_else#1#2#3#4%
4000  {\edef\m_syst_string_one{\detokenize\expandafter{\normalexpanded{#3}}}%
4001   \edef\m_syst_string_two{\detokenize\expandafter{\normalexpanded{#4}}}%
4002   \ifx\m_syst_string_one\m_syst_string_two\expandafter#1\else\expandafter#2\fi}
4003
4004\permanent\protected\def\doifelsesamestring{\syst_helpers_if_samestring_else\firstoftwoarguments\secondoftwoarguments}
4005\permanent\protected\def\doifsamestring    {\syst_helpers_if_samestring_else\firstofoneargument \gobbleoneargument   }
4006\permanent\protected\def\doifnotsamestring {\syst_helpers_if_samestring_else\gobbleoneargument  \firstofoneargument  }
4007
4008\aliased\let\doifsamestringelse\doifelsesamestring
4009
4010%D These are obsolete in MTX:
4011%D
4012%D \starttyping
4013%D \ConvertToConstant    #1#2#3
4014%D \CheckConstantAfter   #1#2
4015%D \ConvertConstantAfter #1#2#3
4016%D \stoptyping
4017
4018%D \macros
4019%D   {assignifempty}
4020%D
4021%D We can assign a default value to an empty macro using:
4022%D
4023%D \starttyping
4024%D \assignifempty \macros {default value}
4025%D \stoptyping
4026%D
4027%D We don't explicitly test if the macro is defined.
4028
4029\permanent\protected\def\assignifempty#1#2%
4030  {\ifempty{#1}\def#1{#2}\fi}
4031
4032%D \macros
4033%D   {gobbleuntil,grabuntil,gobbleuntilrelax,
4034%D    processbetween,processuntil}
4035%D
4036%D In \TEX\ gobbling usually stand for skipping arguments, so here are our gobbling
4037%D macros.
4038%D
4039%D In \CONTEXT\ we use a lot of \type {\start}||\type {\stop} like constructions.
4040%D Sometimes, the \type {\stop} is used as a hard coded delimiter like in: %D
4041%D \starttyping
4042%D \protected\def\startcommand#1\stopcommand%
4043%D   {... #1 ...}
4044%D \stoptyping
4045%D
4046%D In many cases the \type {\start}||\type {\stop} pair is defined at format
4047%D generation time or during a job. This means that we cannot hardcode the \type
4048%D {\stop} criterium. Only after completely understanding \type {\csname} and \type
4049%D {\expandafter} I was able to to implement a solution, starting with:
4050%D
4051%D \starttyping
4052%D \grabuntil{stop}\command
4053%D \stoptyping
4054%D
4055%D This commands executes, after having encountered \type {\stop} the command \type
4056%D {\command}. This command receives as argument the text preceding the \type
4057%D {\stop}. This means that:
4058%D
4059%D \starttyping
4060%D \protected\def\starthello%
4061%D   {\grabuntil{stophello}\message}
4062%D
4063%D \starthello Hello world!\stophello
4064%D \stoptyping
4065%D
4066%D results in: \type{\message{Hello world!}}.
4067
4068\let\syst_helpers_grab_indeed\relax
4069
4070\protected\def\syst_helpers_grab#1#2%
4071  {\def\syst_helpers_grab_indeed##1#1{#2{##1}}\syst_helpers_grab_indeed}
4072
4073\permanent\protected\def\grabuntil#1%
4074  {\expandafter\syst_helpers_grab\expandafter{\csname#1\endcsname}}
4075
4076%D The next command build on this mechanism:
4077%D
4078%D \starttyping
4079%D \processbetween{string}\command
4080%D \stoptyping
4081%D
4082%D Here:
4083%D
4084%D \starttyping
4085%D \processbetween{hello}\message
4086%D \starthello Hello again!\stophello
4087%D \stoptyping
4088%D
4089%D leads to: \type{\message{Hello again!}}. The command
4090%D
4091%D \starttyping
4092%D \gobbleuntil{sequence}
4093%D \stoptyping
4094%D
4095%D is related to these commands. This one simply throws away
4096%D everything preceding \type{\command}.
4097
4098\let\syst_helpers_gobble_indeed\relax
4099
4100\permanent\protected\def\processbetween#1#2%
4101  {\setvalue{\s!start#1}{\grabuntil{\s!stop#1}{#2}}}
4102
4103% \protected\def\gobbleuntil#1%
4104%   {\def\syst_helpers_gobble_indeed##1#1{}\syst_helpers_gobble_indeed}
4105%
4106% \protected\def\gobbleuntilrelax#1\relax
4107%   {}
4108
4109\permanent\protected\def\gobbleuntil#1%
4110  {\def\syst_helpers_gobble_indeed##-#1{}\syst_helpers_gobble_indeed}
4111
4112\permanent\protected\def\gobbleuntilandfinalize#1%
4113  {\def\syst_helpers_gobble_indeed##-#1{#1}\syst_helpers_gobble_indeed}
4114
4115\permanent\protected\def\gobbleuntilrelax#-\relax
4116  {}
4117
4118%D The next one simply expands the pickup up tokens.
4119%D
4120%D \starttyping
4121%D \processuntil{sequence}
4122%D \stoptyping
4123
4124\let\syst_helpers_until_indeed\relax
4125
4126\permanent\protected\def\processuntil#1%
4127  {\def\syst_helpers_until_indeed##1#1{##1}\syst_helpers_until_indeed}
4128
4129%D \macros
4130%D   {groupedcommand}
4131%D
4132%D Commands often manipulate argument as in:
4133%D
4134%D \starttyping
4135%D \def\doezomaarwat#1{....#1....}
4136%D \stoptyping
4137%D
4138%D A disadvantage of this approach is that the tokens that form \type{#1} are fixed
4139%D the the moment the argument is read in. Normally this is no problem, but for
4140%D instance verbatim environments adapt the \CATCODES\ of characters and therefore
4141%D are not always happy with already fixed tokens.
4142%D
4143%D Another problem arises when the argument is grouped not by \type {{}} but by
4144%D \type {\bgroup} and \type {\egroup}. Such an argument fails, because the \type
4145%D {\bgroup} is een as the argument (which is quite normal).
4146%D
4147%D The next macro offers a solution for both unwanted situations:
4148%D
4149%D \starttyping
4150%D \groupedcommand {before} {after}
4151%D \stoptyping
4152%D
4153%D Which can be used like:
4154%D
4155%D \starttyping
4156%D \def\cite%
4157%D   {\groupedcommand{\rightquote\rightquote}{\leftquote\leftquote}}
4158%D \stoptyping
4159%D
4160%D This command is equivalent to, but more 'robust' than:
4161%D
4162%D \starttyping
4163%D \def\cite#1%
4164%D   {\rightquote\rightquote#1\leftquote\leftquote}
4165%D \stoptyping
4166%D
4167%D \starttyping
4168%D \def\rightword%
4169%D   {\groupedcommand{\hfill\hbox}{\parfillskip\zeropoint}}
4170%D
4171%D .......... \rightword{the right way}
4172%D \stoptyping
4173%D
4174%D Here \TEX\ typesets \type {\bf the right way} unbreakable at the end of the line.
4175%D The solution mentioned before does not work here. We also handle
4176%D
4177%D \starttyping
4178%D to be \bold{bold} or not, that's the question
4179%D \stoptyping
4180%D
4181%D and
4182%D
4183%D \starttyping
4184%D to be {\bold bold} or not, that's the question
4185%D \stoptyping
4186%D
4187%D This alternative checks for a \type {\bgroup} token first. The internal
4188%D alternative does not accept the box handling mentioned before, but further
4189%D nesting works all right. The extra \type {\bgroup}||\type {\egroup} is needed to
4190%D keep \type {\m_syst_helpers_handle_group_after} both into sight and local.
4191
4192\let\m_syst_helpers_handle_group_after \relax
4193\let\m_syst_helpers_handle_group_before\relax
4194
4195\protected\def\syst_helpers_handle_group_nop
4196  {\ifnum\currentgrouptype=\semisimplegroupcode
4197     \expandafter\syst_helpers_handle_group_nop_a
4198   \else
4199     \expandafter\syst_helpers_handle_group_nop_b
4200   \fi}
4201
4202\def\syst_helpers_handle_group_nop_a
4203  {\begingroup
4204   \aftergroup\m_syst_helpers_handle_group_a
4205   \aftergroup\endgroup
4206   \m_syst_helpers_handle_group_b}
4207
4208\def\syst_helpers_handle_group_nop_b
4209  {\beginsimplegroup
4210   \aftergroup\m_syst_helpers_handle_group_a
4211   \aftergroup\egroup
4212   \m_syst_helpers_handle_group_b}
4213
4214\protected\def\syst_helpers_handle_group_normal
4215  {\beginsimplegroup
4216   \afterassignment\m_syst_helpers_handle_group_normal_before
4217   \let\next=}
4218
4219\def\m_syst_helpers_handle_group_normal_before
4220  {\beginsimplegroup
4221   \m_syst_helpers_handle_group_b
4222   \bgroup
4223   \aftergroup\m_syst_helpers_handle_group_a
4224   \aftergroup\egroup
4225   \aftergroup\egroup}
4226
4227\protected\def\syst_helpers_handle_group_simple% no inner group (so no kerning interference)
4228  {\beginsimplegroup
4229   \afterassignment\m_syst_helpers_handle_group_simple_before
4230   \let\next=}
4231
4232\def\m_syst_helpers_handle_group_simple_before
4233  {\beginsimplegroup
4234   \aftergroup\m_syst_helpers_handle_group_simple_after
4235   \m_syst_helpers_handle_group_b}
4236
4237\def\m_syst_helpers_handle_group_simple_after
4238  {\m_syst_helpers_handle_group_a
4239   \egroup}
4240
4241\protected\def\syst_helpers_handle_group_pickup% no inner group (so no kerning interference)
4242  {\beginsimplegroup
4243   \afterassignment\m_syst_helpers_handle_group_pickup_before
4244   \let\next=}
4245
4246\def\m_syst_helpers_handle_group_pickup_before
4247  {\beginsimplegroup
4248   \aftergroup\m_syst_helpers_handle_group_a
4249   \aftergroup\egroup
4250   \aftergroup\m_syst_helpers_handle_group_p
4251   \m_syst_helpers_handle_group_b}
4252
4253\protected\def\syst_helpers_handle_group_nop_x
4254  {\ifnum\currentgrouptype=\semisimplegroupcode
4255     \begingroup
4256     \aftergroup\endgroup
4257   \else
4258     \beginsimplegroup
4259     \aftergroup\egroup
4260   \fi
4261   \m_syst_helpers_handle_group_b}
4262
4263\protected\def\syst_helpers_handle_group_normal_x
4264  {\beginsimplegroup
4265   \afterassignment\m_syst_helpers_handle_group_normal_before_x
4266   \let\next=}
4267
4268\def\m_syst_helpers_handle_group_normal_before_x
4269  {\beginsimplegroup
4270   \expandafter\m_syst_helpers_handle_group_b
4271   \ifmmode\beginsimplegroup\else\bgroup\fi % is this save enough? also the other ones?
4272   \aftergroup\egroup
4273   \aftergroup\egroup}
4274
4275%D I considered it a nuisance that
4276%D
4277%D \starttyping
4278%D \color[green]
4279%D   {as grass}
4280%D \stoptyping
4281%D
4282%D was not interpreted as one would expect. This is due to the fact that \type
4283%D {\futurelet} obeys blank spaces, and a line||ending token is treated as a blank
4284%D space. So the final implementation became:
4285
4286\permanent\protected\def\groupedcommand#1#2%
4287  {\def\m_syst_helpers_handle_group_b{#1}%
4288   \def\m_syst_helpers_handle_group_a{#2}%
4289   \futureexpandis\bgroup\syst_helpers_handle_group_normal\syst_helpers_handle_group_nop}
4290
4291\permanent\protected\def\groupedcommandcs#1#2%
4292  {\let\m_syst_helpers_handle_group_b#1%
4293   \let\m_syst_helpers_handle_group_a#2%
4294   \futureexpandis\bgroup\syst_helpers_handle_group_normal\syst_helpers_handle_group_nop}
4295
4296\permanent\protected\def\simplegroupedcommand#1#2%
4297  {\def\m_syst_helpers_handle_group_b{#1}%
4298   \def\m_syst_helpers_handle_group_a{#2}%
4299   \futureexpandis\bgroup\syst_helpers_handle_group_simple\syst_helpers_handle_group_nop}
4300
4301\permanent\protected\def\simplegroupedcommandcs#1#2%
4302  {\let\m_syst_helpers_handle_group_b#1%
4303   \let\m_syst_helpers_handle_group_a#2%
4304   \futureexpandis\bgroup\syst_helpers_handle_group_simple\syst_helpers_handle_group_nop}
4305
4306\permanent\protected\def\pickupgroupedcommand#1#2#3%
4307  {\def\m_syst_helpers_handle_group_b{#1}%
4308   \def\m_syst_helpers_handle_group_a{#2}%
4309   \def\m_syst_helpers_handle_group_p{#3}%
4310   \futureexpandis\bgroup\syst_helpers_handle_group_pickup\syst_helpers_handle_group_nop}
4311
4312\permanent\protected\def\pickupgroupedcommandcs#1#2#3%
4313  {\let\m_syst_helpers_handle_group_b#1%
4314   \let\m_syst_helpers_handle_group_a#2%
4315   \let\m_syst_helpers_handle_group_p#3%
4316   \futureexpandis\bgroup\syst_helpers_handle_group_pickup\syst_helpers_handle_group_nop}
4317
4318\permanent\protected\def\triggergroupedcommand#1%
4319  {\def\m_syst_helpers_handle_group_b{#1}%
4320   \futureexpandis\bgroup\syst_helpers_handle_group_normal_x\syst_helpers_handle_group_nop_x}
4321
4322\permanent\protected\def\triggergroupedcommandcs#1%
4323  {\let\m_syst_helpers_handle_group_b#1%
4324   \futureexpandis\bgroup\syst_helpers_handle_group_normal_x\syst_helpers_handle_group_nop_x}
4325
4326%D Users should be aware of the fact that grouping can interfere with ones paragraph
4327%D settings that are executed after the paragraph is closed. One should therefore
4328%D explictly close the paragraph with \type {\par}, else the settings will be
4329%D forgotten and not applied. So it's:
4330%D
4331%D \starttyping
4332%D \def\BoldRaggedCenter%
4333%D   {\groupedcommand{\raggedcenter\bf}{\par}}
4334%D \stoptyping
4335
4336%D For math we use this:
4337
4338\permanent\protected\def\mathgroupedcommandcs#1%
4339  {\let\m_syst_helpers_handle_group_b#1%
4340   \futureexpandis\bgroup\syst_helpers_handle_math_group_normal\syst_helpers_handle_math_group_nop}
4341
4342\protected\def\syst_helpers_handle_math_group_normal#1%
4343  {\beginmathgroup
4344   \m_syst_helpers_handle_group_b
4345   #1%
4346   \endmathgroup}
4347
4348\protected\def\syst_helpers_handle_math_group_nop
4349  {\m_syst_helpers_handle_group_b}
4350
4351% %D \macros
4352% %D   {checkdefined}
4353% %D
4354% %D The bigger the system, the greater the change that user defined commands collide
4355% %D with those that are part of the system. The next macro gives a warning when a
4356% %D command is already defined. We considered blocking the definition, but this is
4357% %D not always what we want.
4358% %D
4359% %D \starttyping
4360% %D \checkdefined {category} {class} {command}
4361% %D \stoptyping
4362% %D
4363% %D The user is warned with the suggestion to use \type {CAPITALS}. This suggestion
4364% %D is feasible, because \CONTEXT only defines lowcased macros. These are obsolete:
4365%
4366% \protected\def\showdefinederror#1#2%
4367%   {\writestatus\m!system{#1 #2 replaces a macro, use CAPITALS!}}
4368%
4369% \protected\def\checkdefined#1#2#3%
4370%   {\doifdefined{#3}{\showdefinederror{#2}{#3}}}
4371
4372%D \macros
4373%D   {GotoPar,GetPar}
4374%D
4375%D Typesetting a paragraph in a special way can be done by first grabbing the
4376%D contents of the paragraph and processing this contents grouped. The next macro
4377%D for instance typesets a paragraph in boldface.
4378%D
4379%D \starttyping
4380%D \def\remark#1\par%
4381%D   {\bgroup\bf#1\egroup}
4382%D \stoptyping
4383%D
4384%D This macro has to be called like
4385%D
4386%D \starttyping
4387%D \remark some text ... ending with \par
4388%D \stoptyping
4389%D
4390%D Instead of \type {\par} we can of course use an empty line. When we started
4391%D typesetting with \TEX, we already had produced lots of text in plain \ASCII. In
4392%D producing such simple formatted texts, we adopted an open layout, and when
4393%D switching to \TEX, we continued this open habit. Although \TEX\ permits a cramped
4394%D and badly formatted source, it adds to confusion and sometimes introduces errors.
4395%D So we prefer:
4396%D
4397%D \starttyping
4398%D \remark
4399%D
4400%D some text ... ending with an empty line
4401%D \stoptyping
4402%D
4403%D We are going to implement a mechanism that allows such open specifications. The
4404%D definition of the macro handling \type {\remark} becomes:
4405%D
4406%D \starttyping
4407%D \def\remark%
4408%D   {\BeforePar{\bgroup\bf}%
4409%D    \AfterPar{\egroup}%
4410%D    \GetPar}
4411%D \stoptyping
4412%D
4413%D A macro like \type {\GetPar} can be defined in several ways. The recent version,
4414%D the fourth one in a row, originally was far more complicated, but some
4415%D functionality has been moved to other macros.
4416%D
4417%D We start with the more simple but in some cases more appropriate alternative is
4418%D \type {\GotoPar}. This one leaves \type {\par} unchanged and is therefore more
4419%D robust. On the other hand, \type {\AfterPar} is not supported.
4420
4421\let\syst_helpers_par_before\relax
4422\let\syst_helpers_par_around\relax
4423
4424% \permanent\protected\def\dowithpar#1#2%
4425%   {\globalpushmacro\syst_helpers_par_around
4426%    \def\syst_helpers_par_around##1\par{#1##1#2\globalpopmacro\syst_helpers_par_around}%
4427%    \expandafter\syst_helpers_par_around\ignorepars}
4428
4429% \permanent\protected\def\dogotopar#1%
4430%   {\globalpushmacro\syst_helpers_par_before
4431%    \def\syst_helpers_par_before{#1\globalpopmacro\syst_helpers_par_before}%
4432%    \expandafter\syst_helpers_par_before\ignorepars}
4433
4434\permanent\protected\def\dowithpar#1#2%
4435  {\begingroup\aftergrouped{#1\dontleavehmode\wrapuppar{#2}}\expandafter\endgroup\ignorepars}
4436
4437\permanent\protected\def\dogotopar#1%
4438  {\begingroup\aftergrouped{#1}\expandafter\endgroup\ignorepars}
4439
4440\aliased\let\dogotoparcs\dogotopar
4441
4442\permanent\protected\def\dogotoparstart
4443  {\ignorepars}
4444
4445%D This is old and kind of obsolete:
4446
4447\newtoks\BeforePar
4448\newtoks\AfterPar
4449
4450\permanent\protected\def\GetPar
4451  {\normalexpanded
4452     {\dowithpar
4453        {\the\BeforePar
4454         \BeforePar\emptytoks}
4455        {\the\AfterPar
4456         \BeforePar\emptytoks
4457         \AfterPar\emptytoks}}}
4458
4459\permanent\protected\def\GotoPar
4460  {\normalexpanded
4461     {\dogotopar
4462        {\the\BeforePar
4463         \BeforePar\emptytoks}}}
4464
4465%D \macros
4466%D   {dowithpargument,dowithwargument}
4467%D
4468%D The next macros are a variation on \type {\GetPar}. When macros expect an
4469%D argument, it interprets a grouped sequence of characters a one token. While this
4470%D adds to robustness and less ambiguous situations, we sometimes want to be a bit
4471%D more flexible, or at least want to be a bit more tolerant to user input.
4472%D
4473%D We start with a commands that acts on paragraphs. This
4474%D command is called as:
4475%D
4476%D \starttyping
4477%D \dowithpargument\command
4478%D \dowithpargument{\command ... }
4479%D \stoptyping
4480%D
4481%D In \CONTEXT\ we use this one to read in the titles of chapters, sections etc. The
4482%D commands responsible for these activities accept several alternative ways of
4483%D argument passing. In these examples, the \type {\par} can be omitted when an
4484%D empty line is present.
4485%D
4486%D \starttyping
4487%D \command{...}
4488%D \command ... \par
4489%D \command
4490%D   {...}
4491%D \command
4492%D   ... \par
4493%D \stoptyping
4494
4495\let\syst_helpers_next_par\relax
4496\let\syst_helpers_next_arg\relax
4497
4498\permanent\protected\def\dowithpargument#1%
4499  {\def\syst_helpers_next_par##1 \par{#1{##1}}%
4500   \def\syst_helpers_next_arg##1{#1{##1}}%
4501   \doifelsenextbgroup\syst_helpers_next_arg{\doifelsenextchar\par{#1{}}\syst_helpers_next_par}}
4502
4503%D The \type {p} in the previous command stands for paragraph. When we want to act
4504%D upon words we can use the \type{w} alternative.
4505%D
4506%D \starttyping
4507%D \dowithwargument\command
4508%D \dowithwargument{... \command ...}
4509%D \stoptyping
4510%D
4511%D The main difference bwteen two alternatives is in the handling of \type {\par}'s.
4512%D This time the space token acts as a delimiter.
4513%D
4514%D \starttyping
4515%D \command{...}
4516%D \command ...
4517%D \command
4518%D   {...}
4519%D \command
4520%D   ...
4521%D \stoptyping
4522
4523\let\syst_helpers_next_war\relax
4524\let\syst_helpers_next_arg\relax
4525
4526\permanent\protected\def\dowithwargument#1%
4527  {\def\syst_helpers_next_war##1 {#1{##1}}%
4528   \def\syst_helpers_next_arg##1{#1{##1}}%
4529   \doifelsenextbgroup\syst_helpers_next_arg\syst_helpers_next_war}
4530
4531%D \macros
4532%D   {dorepeat,dorepeatwithcommand}
4533%D
4534%D When doing repetitive tasks, we stromgly advice to use \type {\dorecurse}. The
4535%D next alternative however, suits better some of the \CONTEXT\ interface commands.
4536%D
4537%D \starttyping
4538%D \dorepeat[n*\command]
4539%D \stoptyping
4540%D
4541%D The value of the used \COUNTER\ can be called within
4542%D \type{\command} by \type{\repeater}.
4543%D
4544%D A slightly different alternative is:
4545%D
4546%D \starttyping
4547%D \dorepeatwithcommand[n*{...}]\command
4548%D \stoptyping
4549%D
4550%D When we call for something like:
4551%D
4552%D \starttyping
4553%D \dorepeatwithcommand[3*{Hello}]\message
4554%D \stoptyping
4555%D
4556%D we get ourselves three \type {\message{Hello}} messages in a row. In both
4557%D commands, the \type {n*} is optional. When this specification is missing, the
4558%D command executes once.
4559
4560\permanent\protected\def\dorepeatwithcommand[#1]%
4561  {\syst_helpers_repeat_with_command#1*\empty*\relax}
4562
4563\def\syst_helpers_repeat_with_command#1*#2#3*#4\relax#5%
4564  {\ifx#2\empty\syst_helpers_repeat_with_command_again[#1]#5\else\syst_helpers_repeat_with_command_indeed{#1}{#2}{#3}#5\fi}
4565
4566\def\syst_helpers_repeat_with_command_indeed#1#2#3#4%
4567  {\ifx#2\empty % redundant but gives cleaner extensions
4568     #4{#1}%
4569   \orelse\ifnum#1<\zerocount
4570    %\normalexpanded{\dorecurse{\number-\number#1}}{#4{-#2#3}}%
4571     \dorecurse{-#1}{#4{-#2#3}}%
4572   \orelse\ifx#2+%
4573     \dorecurse{#1}{#4{#3}}%
4574   \else
4575     \dorecurse{#1}{#4{#2#3}}%
4576   \fi}
4577
4578\def\syst_helpers_repeat_with_command_again[#1]#2%
4579  {#2{#1}}
4580
4581%D The extension hook permits something like:
4582%D
4583%D \starttyping
4584%D \bgroup
4585%D
4586%D \catcode`\*=\superscriptcatcode
4587%D
4588%D \gdef\syst_helpers_repeat_with_command_again[#1]%
4589%D   {\redodorepeatwithcommand#1*\empty*\relax}
4590%D
4591%D \gdef\redodorepeatwithcommand#1*#2#3*#4\relax#5%
4592%D   {\syst_helpers_repeat_with_command_indeed{#1}{#2}{#3}#5}
4593%D
4594%D \egroup
4595%D \stoptyping
4596%D
4597%D although one may wonder if changing the catcode of \type {*} is wise.
4598
4599%D \macros
4600%D   {doifstringinstringelse}
4601%D
4602%D The next macro is meant for situations where both strings are macros. This save
4603%D some unneeded expansion. But now we just alias.
4604
4605\aliased\let\doifelsestringinstring\doifelseinstring
4606\aliased\let\doifstringinstringelse\doifelseinstring
4607
4608%D \macros
4609%D   {appendtoks,prependtoks,appendtoksonce,prependtoksonce,
4610%D    doifintokselse,flushtoks,dotoks}
4611%D
4612%D We use tokenlists sparsely within \CONTEXT, because the comma separated lists are
4613%D more suitable for the user interface. Nevertheless we have:
4614%D
4615%D \starttyping
4616%D (\doglobal) \appendtoks ... \to\tokenlist
4617%D (\doglobal) \prependtoks ... \to\tokenlist
4618%D (\doglobal) \flushtoks\tokenlist
4619%D             \dotoks\tokenlist
4620%D \stoptyping
4621%D
4622%D These macros are clones of the ones implemented in page~378 of Knuth's \TEX book.
4623
4624\newtoks\t_syst_helpers_scratch
4625
4626\lettonothing\m_syst_helpers_scratch
4627
4628\permanent\protected\def\appendtoks#1\to#2%
4629  {\ifrelax\dodoglobal
4630     \expandafter\toksapp
4631   \else
4632     \resetglobal
4633     \expandafter\gtoksapp
4634   \fi#2{#1}}
4635
4636\permanent\protected\def\prependtoks#1\to#2%
4637  {\ifrelax\dodoglobal
4638     \expandafter\tokspre
4639   \else
4640     \resetglobal
4641     \expandafter\gtokspre
4642   \fi#2{#1}}
4643
4644\permanent\protected\def\appendtoksonce#1\to#2%
4645  {\ifhasxtoks{#1}#2\else
4646     \toksapp#2{#1}%
4647   \fi}
4648
4649\permanent\protected\def\prependtoksonce#1\to#2%
4650  {\ifhasxtoks{#1}#2\else
4651     \tokspre#2{#1}%
4652   \fi}
4653
4654% Soon (check usage):
4655%
4656% \permanent\protected\def\appendtoks #1\to#2{\toksapp#2{#1}}
4657% \permanent\protected\def\prependtoks#1\to#2{\tokspre#2{#1}}
4658%
4659% \permanent\protected\def\appendtoksonce #1\to#2{\ifhasxtoks{#1}#2\else\toksapp#1{#1}\fi}
4660% \permanent\protected\def\prependtoksonce#1\to#2{\ifhasxtoks{#1}#2\else\tokspre#2{#1}\fi}
4661
4662%D The test macro:
4663
4664\permanent\protected\def\doifelseintoks#1#2% #1 en #2 zijn toks
4665  {\ifhasxtoks#1#2%
4666     \expandafter\firstoftwoarguments
4667   \else
4668     \expandafter\secondoftwoarguments
4669   \fi}
4670
4671\aliased\let\doifintokselse\doifelseintoks
4672
4673%D Moved from \type {lxml-ini.tex} to here. This one is for generators that collect
4674%D stuff piecewise, which is sometimes hard on mechanisms that grab content using
4675%D delimiters:
4676%D
4677%D \starttyping
4678%D \startcollecting
4679%D \startcollect \bTABLE \stopcollect
4680%D     \startcollect \bTR \stopcollect
4681%D         \startcollect \bTD \stopcollect
4682%D         \startcollect   foo\stopcollect
4683%D         \startcollect \eTD \stopcollect
4684%D         \startcollect \bTD \stopcollect
4685%D         \startcollect   bar\stopcollect
4686%D         \startcollect \eTD \stopcollect
4687%D     \startcollect \eTR \stopcollect
4688%D \startcollect \eTABLE \stopcollect
4689%D \stopcollecting
4690%D \stoptyping
4691
4692\newtoks \collectingtoks
4693
4694\permanent\protected\lettonothing\stopcollect
4695\permanent\protected\lettonothing\stopexpandedcollect
4696
4697\permanent\protected\def\startcollect        #1\stopcollect        {\toksapp \collectingtoks{#1}}
4698\permanent\protected\def\startexpandedcollect#1\stopexpandedcollect{\etoksapp\collectingtoks{#1}}
4699
4700\permanent\protected\def\startcollecting{\collectingtoks\emptytoks}
4701\permanent\protected\def\stopcollecting {\the\collectingtoks}
4702
4703\permanent\protected\def\collect        {\toksapp \collectingtoks}
4704\permanent\protected\def\collectexpanded{\etoksapp\collectingtoks}
4705
4706%D A nice one too:
4707
4708% {\scratchtoks{abc} \removetoks b\from\scratchtoks [\the\scratchtoks]}
4709% {\scratchtoks{abc} \removetoks x\from\scratchtoks [\the\scratchtoks]}
4710% {\scratchtoks{} \removetoks x\from\scratchtoks [\the\scratchtoks]}
4711% {\scratchtoks{xaa} \removetoks x\from\scratchtoks [\the\scratchtoks]}
4712% {\scratchtoks{a\relax b} \removetoks \relax\from\scratchtoks [\showthe\scratchtoks]}
4713
4714\let\syst_helpers_remove_toks\relax
4715
4716\permanent\protected\def\removetoks#1\from#2%
4717  {\def\syst_helpers_remove_toks##1#1##2\empty\empty\empty##3^^^^0004%
4718     {\def\m_syst_string_one{##3}%
4719      \ifempty\m_syst_string_one#2{##1}\else#2{##1##2}\fi}%
4720   \expandafter\syst_helpers_remove_toks\the#2\empty\empty\empty#1\empty\empty\empty^^^^0004}
4721
4722%D Also:
4723
4724\permanent\protected\def\appendetoks#1\to#2%
4725  {\ifrelax\dodoglobal
4726     \expandafter\etoksapp
4727   \else
4728     \resetglobal
4729     \expandafter\xtoksapp
4730   \fi#2{#1}}
4731
4732\permanent\protected\def\prependetoks#1\to#2%
4733  {\ifrelax\dodoglobal
4734     \expandafter\etokspre
4735   \else
4736     \resetglobal
4737     \expandafter\xtokspre
4738   \fi#2{#1}}
4739
4740%D Hm.
4741
4742\permanent\protected\def\flushtoks#1% nb: can reassign to #1 again, hence the indirectness
4743  {\t_syst_helpers_scratch#1\relax
4744   \dodoglobal#1\emptytoks
4745   \the\t_syst_helpers_scratch\relax}
4746
4747\aliased\let\dotoks\the
4748
4749%D \macros
4750%D   {beforesplitstring,aftersplitstring}
4751%D
4752%D These both commands split a string at a given point in two
4753%D parts, so \type{x.y} becomes \type{x} or \type{y}.
4754%D
4755%D \starttyping
4756%D \beforesplitstring test.tex\at.\to\filename
4757%D \aftersplitstring  test.tex\at.\to\extension
4758%D \stoptyping
4759%D
4760%D The first routine looks (and is indeed) a bit simpler than the second one. The
4761%D alternative looking more or less like the first one did not always give the
4762%D results we needed. Both implementations show some insight in the manipulation of
4763%D arguments.
4764
4765\let\syst_helpers_split_string\relax
4766
4767\permanent\protected\def\beforesplitstring#1\at#2\to#3%
4768  {\def\syst_helpers_split_string##1#2##0^^^^0004% no #- as we need to count
4769     {\ifarguments
4770        \lettonothing#3%
4771      \or
4772        \lettonothing#3%
4773      \else
4774        \def#3{##1}%
4775     \fi}%
4776   \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments}
4777
4778\permanent\protected\def\aftersplitstring#1\at#2\to#3%
4779  {\def\syst_helpers_split_string##0#2##2^^^^0004% no #- as we need to count
4780     {\ifarguments
4781        \lettonothing#3%
4782      \or
4783        \def#3{#1}%
4784      \else
4785        \def#3{##2}%
4786     \fi}%
4787   \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments}
4788
4789%D \macros
4790%D   {splitstring,greedysplitstring}
4791%D
4792%D A bonus macro.
4793
4794\permanent\protected\def\splitstring#1\at#2\to#3\and#4%
4795  {\def\syst_helpers_split_string##1#2##2^^^^0004%
4796     {\ifarguments
4797        \lettonothing#3*
4798        \lettonothing#4*
4799      \or
4800        \def#3{#1}%
4801        \lettonothing#4%
4802      \else
4803        \def#3{##1}%
4804        \def#4{##2}%
4805     \fi}%
4806   \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments}
4807
4808\permanent\protected\def\greedysplitstring#1\at#2\to#3\and#4%
4809  {\def\syst_helpers_split_string##1#2##2^^^^0004%
4810     {\ifarguments
4811        \lettonothing#3%
4812        \lettonothing#4%
4813      \or
4814        \def#3{#1}%
4815        \lettonothing#4%
4816      \else
4817        \def#3{##1}%
4818        \def#4{##2}%
4819        \def\syst_helpers_split_string####1#2####2^^^^0004%
4820          {\ifarguments
4821           \or
4822           \else
4823             \expandafter\def\expandafter#3\expandafter{#3####1}%
4824             \def#4{####2}%
4825             \syst_helpers_split_string####2^^^^0004\ignorearguments\ignorearguments
4826          \fi}%
4827        \syst_helpers_split_string##2^^^^0004\ignorearguments\ignorearguments
4828     \fi}%
4829   \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments}
4830
4831%D \macros
4832%D   {beforetestandsplitstring,
4833%D    aftertestandsplitstring,
4834%D    testandsplitstring}
4835
4836\aliased\let\beforetestandsplitstring\beforesplitstring
4837
4838\permanent\protected\def\aftertestandsplitstring#1\at#2\to#3%
4839  {\def\syst_helpers_split_string##0#2##2^^^^0004% no #- as we need to count
4840     {\ifarguments
4841        \lettonothing#3%
4842      \or
4843        \lettonothing#3%
4844      \else
4845        \def#3{##2}%
4846     \fi}%
4847   \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments}
4848
4849\permanent\protected\def\testandsplitstring#1\at#2\to#3\and#4%
4850  {\def\syst_helpers_split_string##1#2##2^^^^0004%
4851     {\ifarguments
4852        \lettonothing#3%
4853        \lettonothing#4%
4854      \or
4855        \lettonothing#3%
4856        \lettonothing#4%
4857      \else
4858        \def#3{##1}%
4859        \def#4{##2}%
4860     \fi}%
4861   \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments}
4862
4863%D \macros
4864%D   {splitatperiod, splitatcomma, splitatasterisk, splitatcolon, splitatcolons}
4865
4866\protected\def\syst_helpers_splitatperiod    #1.#2.#-^^^^0004#3#4{\def #3{#1}\def #4{#2}}
4867\protected\def\syst_helpers_splitatcomma     #1,#2,#-^^^^0004#3#4{\def #3{#1}\def #4{#2}}
4868\protected\def\syst_helpers_splitatasterisk  #1*#2*#-^^^^0004#3#4{\def #3{#1}\def #4{#2}}
4869\protected\def\syst_helpers_splitatcolon     #1:#2:#-^^^^0004#3#4{\def #3{#1}\def #4{#2}}
4870\protected\def\syst_helpers_splitatcolons  #1::#2::#-^^^^0004#3#4{\edef#3{#1}\edef#4{#2}}
4871
4872\permanent\protected\def\splitatperiod  #1{\normalexpanded{\syst_helpers_splitatperiod  #1}..^^^^0004}
4873\permanent\protected\def\splitatcomma   #1{\normalexpanded{\syst_helpers_splitatcomma   #1},,^^^^0004}    % not at ", "
4874\permanent\protected\def\splitatasterisk#1{\normalexpanded{\syst_helpers_splitatasterisk#1}**^^^^0004}
4875\permanent\protected\def\splitatcolon   #1{\normalexpanded{\syst_helpers_splitatcolon   #1}::^^^^0004}
4876\permanent\protected\def\splitatcolons  #1{\normalexpanded{\syst_helpers_splitatcolons  #1}::::^^^^0004}
4877
4878%D \macros
4879%D   {removesubstring}
4880%D
4881%D A first application of the two routines defined above is:
4882%D
4883%D \starttyping
4884%D \removesubstring-\from first-last\to\nothyphenated
4885%D \stoptyping
4886%D
4887%D Which in terms of \TEX\ looks like:
4888
4889\permanent\protected\def\removesubstring#1\from#2\to#3%
4890  {\splitstring#2\at#1\to\m_syst_string_one\and\m_syst_string_two
4891   \dodoglobal\def#3{\m_syst_string_one\m_syst_string_two}}
4892
4893%D \macros
4894%D   {appendtocommalist,prependtocommalist,
4895%D    addtocommalist,removefromcommalist}
4896%D
4897%D When working with comma separated lists, one sooner or later want the tools to
4898%D append or remove items from such a list. When we add an item, we first check if
4899%D it's already there. This means that every item in the list is unique.
4900%D
4901%D \starttyping
4902%D \addtocommalist      {alfa}  \name
4903%D \addtocommalist      {beta}  \name
4904%D \addtocommalist      {gamma} \name
4905%D \removefromcommalist {beta}  \name
4906%D \stoptyping
4907%D
4908%D These commands can be prefixed with \type {\doglobal}. The implementation of the
4909%D second command is more complecated, because we have to take leading spaces into
4910%D account. Keep in mind that users may provide lists with spaces after the commas.
4911%D When one item is left, we also have to get rid of trailing spaces.
4912%D
4913%D \starttyping
4914%D \def\words{alfa, beta, gamma, delta}
4915%D \def\words{alfa,beta,gamma,delta}
4916%D \stoptyping
4917%D
4918%D Removing an item takes more time than adding one. A fast appending alternative,
4919%D without any testing, is also provided:
4920%D
4921%D \starttyping
4922%D \appendtocommalist  {something} \name
4923%D \prependtocommalist {something} \name
4924%D \stoptyping
4925%D
4926%D This can be implemented as follows:
4927%D
4928%D \starttyping
4929%D \def\appendtocommalist#1#2%
4930%D   {\ifx#2\empty
4931%D      \dodoglobal\edef#2{#1}%
4932%D    \else % no test on empty
4933%D      \dodoglobal\edef#2{#2,#1}%
4934%D    \fi}
4935%D
4936%D \def\prependtocommalist#1#2%
4937%D   {\ifx#2\empty
4938%D      \dodoglobal\edef#2{#1}%
4939%D    \else % no test on empty
4940%D      \dodoglobal\edef#2{#1,#2}%
4941%D    \fi}
4942%D \stoptyping
4943%D
4944%D The faster alternatives are:
4945
4946\permanent\protected\def\appendtocommalist#1#2%
4947  {\dodoglobal\edef#2{\ifempty#2\else#2,\fi#1}}
4948
4949\permanent\protected\def\prependtocommalist#1#2%
4950  {\dodoglobal\edef#2{#1\ifempty#2\else,#2\fi}}
4951
4952\permanent\protected\def\addtocommalist#1#2% {item} \cs
4953  {\rawdoifelseinset{#1}#2\resetglobal
4954     {\dodoglobal\edef#2{\ifempty#2\else#2,\fi#1}}}
4955
4956\permanent\protected\def\pretocommalist#1#2% {item} \cs
4957  {\rawdoifelseinset{#1}#2\resetglobal
4958     {\dodoglobal\edef#2{#1\ifempty#2\else,#2\fi}}}
4959
4960\permanent\protected\def\robustdoifelseinset#1#2%
4961  {\edef\m_syst_string_one{\detokenize\expandafter{\normalexpanded{#1}}}%
4962   \edef\m_syst_string_two{\detokenize\expandafter{\normalexpanded{#2}}}%
4963   \rawdoifelseinset\m_syst_string_one\m_syst_string_two}
4964
4965\aliased\let\robustdoifinsetelse\robustdoifelseinset
4966
4967\permanent\protected\def\robustaddtocommalist#1#2% {item} \cs
4968  {\robustdoifelseinset{#1}#2\resetglobal
4969     {\dodoglobal\edef#2{\ifempty#2\else#2,\fi#1}}}
4970
4971\permanent\protected\def\robustpretocommalist#1#2% {item} \cs
4972  {\robustdoifelseinset{#1}#2\resetglobal
4973     {\dodoglobal\edef#2{#1\ifempty#2\else,#2\fi}}}
4974
4975\permanent\protected\def\xsplitstring#1#2% \cs {str}
4976  {\def\syst_helpers_split_string##1,#2,##2,#2,##-\\%
4977     {\edef\m_syst_string_one{\syst_cleanedup_commalist_b##1\empty\empty\relax}%
4978      \edef\m_syst_string_two{\syst_cleanedup_commalist_a##2,,\relax}}%
4979   \expandafter\syst_helpers_split_string\expandafter,#1,,#2,,#2,\\}
4980
4981\def\syst_cleanedup_commalist_b#1#2\relax{\if#1,\else#1\fi#2}
4982\def\syst_cleanedup_commalist_a#1,,#-\relax{#1}
4983
4984\permanent\protected\def\removefromcommalist#1#2% to be sped up
4985  {\rawdoifelseinset{#1}#2%
4986     {\normalexpanded{\xsplitstring\noexpand#2{#1}}%
4987      \dodoglobal\edef#2%
4988        {\ifempty\m_syst_string_one
4989           \m_syst_string_two
4990         \else
4991           \m_syst_string_one\ifempty\m_syst_string_two\else,\m_syst_string_two\fi
4992         \fi}}
4993     \resetglobal}
4994
4995%D \macros
4996%D   {substituteincommalist}
4997%D
4998%D Slow but seldom used, so for the moment we stick to this implementation.
4999%D
5000%D \starttyping
5001%D \substituteincommalist{old}{new}{list}
5002%D \stoptyping
5003
5004\def\syst_helpers_substitute_in_comma_list_step#1%
5005  {\edef\m_syst_string_three{#1}%
5006   \ifx\m_syst_string_one\m_syst_string_three
5007     \ifempty\m_syst_string_two \else
5008       \edef\m_syst_string_four{\ifempty\m_syst_string_four\else\m_syst_string_four,\fi\m_syst_string_two}%
5009     \fi
5010   \else
5011     \edef\m_syst_string_four{\ifempty\m_syst_string_four\else\m_syst_string_four,\fi#1}%
5012   \fi}
5013
5014\permanent\protected\def\substituteincommalist#1#2#3% old, new, list (slooow)
5015  {\edef\m_syst_string_one{#1}%
5016   \edef\m_syst_string_two{#2}%
5017   \lettonothing\m_syst_string_four
5018   \normalexpanded{\rawprocesscommacommand[#3]}\syst_helpers_substitute_in_comma_list_step
5019   \let#3\m_syst_string_four}
5020
5021%D \macros
5022%D   {replaceincommalist}
5023%D
5024%D The next macro can be used to replace an indexed element in a commalist:
5025%D
5026%D \starttyping
5027%D \replaceincommalist\MyList{2}
5028%D \stoptyping
5029%D
5030%D Element~2 will be replaced by the current meaning of the macro \type
5031%D {\newcommalistelement}. The old meaning is saved in \type {\commalistelement}.
5032%D The replacement honors grouped items, like in:
5033%D
5034%D \starttyping
5035%D \def\MyList{a,b,c,d,e,f}   \replaceincommalist\MyList{3}
5036%D \def\MyList{a,b,c,d,e,f}   \replaceincommalist\MyList{3}
5037%D \def\MyList{a,{b,c},d,e,f} \replaceincommalist\MyList{3}
5038%D \def\MyList{a,b,c,{d,e,f}} \replaceincommalist\MyList{3}
5039%D \stoptyping
5040%D
5041%D This macro was used in the bibtex code (and is probably no longer needed).
5042
5043\newinteger\c_syst_helpers_comma_list_index
5044
5045\lettonothing\m_syst_helpers_comma_list_target
5046
5047\mutable\lettonothing\newcommalistelement
5048
5049\def\syst_helpers_replace_in_comma_list_step#1% we can use #+ here too
5050  {\ifnum\commalistcounter=\c_syst_helpers_comma_list_index\relax
5051     \ifempty\newcommalistelement\else
5052       \ifempty\m_syst_helpers_comma_list_target
5053         \let\m_syst_helpers_comma_list_target\newcommalistelement
5054       \else
5055         \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter
5056           \m_syst_helpers_comma_list_target\expandafter\expandafter\expandafter
5057             {\expandafter\m_syst_helpers_comma_list_target\expandafter,\newcommalistelement}%
5058       \fi
5059     \fi
5060     \def\commalistelement{#1}%
5061   \else
5062     \ifempty\m_syst_helpers_comma_list_target
5063       \ifx\nexttoken\bgroup % is known -)
5064         \def\m_syst_helpers_comma_list_target{{#1}}%
5065       \else
5066         \def\m_syst_helpers_comma_list_target{#1}%
5067       \fi
5068     \else
5069       \ifx\nexttoken\bgroup % is known -)
5070         \expandafter\def\expandafter\m_syst_helpers_comma_list_target\expandafter{\m_syst_helpers_comma_list_target,{#1}}%
5071       \else
5072         \expandafter\def\expandafter\m_syst_helpers_comma_list_target\expandafter{\m_syst_helpers_comma_list_target,#1}%
5073       \fi
5074     \fi
5075   \fi
5076   \advanceby\commalistcounter\plusone}
5077
5078\permanent\protected\def\replaceincommalist#1#2% #1 = commalistelement #2 = position starts at 1
5079  {\c_syst_helpers_comma_list_index#2\relax
5080   \lettonothing\m_syst_helpers_comma_list_target
5081   \lettonothing\commalistelement
5082   \commalistcounter\plusone
5083   \expandafter\processcommalist\expandafter[#1]\syst_helpers_replace_in_comma_list_step
5084   \dodoglobal\let#1\m_syst_helpers_comma_list_target}
5085
5086%D \macros
5087%D   {globalprocesscommalist}
5088%D
5089%D The commalist processing commands are characterized by the fact that the way they
5090%D handle expansion as well as the fact that they can be nested. This makes them
5091%D kind of useless for handling comma lists in alignments. In these situations the
5092%D next macro can be of use.
5093
5094\lettonothing\m_syst_helpers_comma_list_command_global
5095
5096\def\syst_helpers_comma_list_command_global_step#1,%
5097  {\if]#1\else
5098     \m_syst_helpers_comma_list_command_global{#1}%
5099     \expandafter\syst_helpers_comma_list_command_global_step
5100   \fi}
5101
5102\permanent\protected\def\globalprocesscommalist[#1]#2%
5103  {\glet\m_syst_helpers_comma_list_command_global#2%
5104   \expandafter\syst_helpers_comma_list_command_global_step#1,],}
5105
5106%D \macros
5107%D   {withoutpt,PtToCm,
5108%D    numberofpoints,dimensiontocount}
5109%D
5110%D We can convert point into centimeters with:
5111%D
5112%D \starttyping
5113%D \PtToCm{dimension}
5114%D \stoptyping
5115
5116% {\catcode`\.=\othercatcode
5117%  \catcode`\p=\othercatcode
5118%  \catcode`\t=\othercatcode
5119%  \gdef\WITHOUTPT#1pt{#1}}
5120%
5121% \def\withoutpt#1%
5122%   {\expandafter\WITHOUTPT#1}
5123%
5124% Okay, not really an impressive extension to the engine, but better than some macro
5125% that is demonstrating deeper understanding of \TEX. It's anyway a trivial extension
5126% anyway. There are actually many examples of very advanced macros and exposure of
5127% how clever one is that could be done cheap in the engine. It's just that at the time
5128% that \TEX\ was written, it made no sense to add a lot of that. After decades we know
5129% what extras we need.
5130
5131% \permanent\def\withoutpt#1{\thewithoutunit\dimexpr#1}
5132
5133\aliased\let\withoutpt\toscaled
5134
5135%D The capitals are needed because \type {p} and \type {t} have catcode~12, while
5136%D macronames only permit tokens with the catcode~11. As a result we cannot use the
5137%D \type {.group} primitives. Those who want to know more about this kind of
5138%D manipulations, we advice to study the \TEX book in detail. Because this macro
5139%D does not do any assignment, we can use it in the following way too.
5140
5141% \permanent\def\PtToCm#1{\thewithoutunit\dimexpr0.0351459804\dimexpr#1\relax\relax cm}
5142
5143\permanent\def\PtToCm#1{\toscaled\dimexpr0.0351459804\dimexpr#1\relax\relax cm}
5144
5145%D We also support:
5146%D
5147%D \starttyping
5148%D \numberofpoints   {dimension}
5149%D \dimensiontocount {dimension} {\count}
5150%D \stoptyping
5151%D
5152%D Both macros return a rounded number.
5153
5154% \dimensiontocount{10.49pt}\scratchcounter \the\scratchcounter / \numberofpoints{10.49pt}
5155% \dimensiontocount{10.51pt}\scratchcounter \the\scratchcounter / \numberofpoints{10.51pt}
5156
5157\permanent\def\dimensiontocount#1#2{#2\numexpr\dimexpr#1\relax/\maxcard\relax}
5158\permanent\def\numberofpoints    #1{\the\numexpr\dimexpr#1\relax/\maxcard\relax}
5159
5160%D \macros
5161%D   {swapdimens,swapskips,swapcounts,swapmacros,
5162%D    globalswapdimens,globalswapcounts,globalswapmacros}
5163%D
5164%D Simple but effective are the next two macros. There name exactly states their
5165%D purpose.
5166
5167\newdimension\d_syst_helpers_swapped
5168\newgluespec \s_syst_helpers_swapped
5169\newinteger  \c_syst_helpers_swapped
5170\let         \m_syst_helpers_swapped\relax
5171
5172% \protected\def\swapdimens#1#2{\d_syst_helpers_swapped#1#1#2#2\d_syst_helpers_swapped}
5173% \protected\def\swapskips #1#2{\s_syst_helpers_swapped#1#1#2#2\s_syst_helpers_swapped}
5174% \protected\def\swapcounts#1#2{\c_syst_helpers_swapped#1#1#2#2\c_syst_helpers_swapped}
5175% \protected\def\swapmacros#1#2{\let\m_syst_helpers_swapped#1\let#1#2\let#2\m_syst_helpers_swapped}
5176
5177\aliased\let\swapdimens\swapcsvalues
5178\aliased\let\swapskips \swapcsvalues
5179\aliased\let\swapcounts\swapcsvalues
5180\aliased\let\swapmacros\swapcsvalues
5181
5182% \protected\def\globalswapdimens#1#2{\d_syst_helpers_swapped#1\global#1#2\global#2\d_syst_helpers_swapped}
5183% \protected\def\globalswapskips #1#2{\s_syst_helpers_swapped#1\global#1#2\global#2\s_syst_helpers_swapped}
5184% \protected\def\globalswapcounts#1#2{\c_syst_helpers_swapped#1\global#1#2\global#2\c_syst_helpers_swapped}
5185% \protected\def\globalswapmacros#1#2{\let\m_syst_helpers_swapped#1\glet#1#2\glet#2\m_syst_helpers_swapped}
5186
5187\permanent\protected\def\globalswapdimens{\global\swapcsvalues}
5188\permanent\protected\def\globalswapskips {\global\swapcsvalues}
5189\permanent\protected\def\globalswapcounts{\global\swapcsvalues}
5190\permanent\protected\def\globalswapmacros{\global\swapcsvalues}
5191
5192%D \macros
5193%D   {pushmacro,popmacro}
5194%D
5195%D Premature and a bit of beta, we offer:
5196%D
5197%D \starttyping
5198%D \pushmacro\macro
5199%D \popmacro\macro
5200%D \stoptyping
5201
5202\permanent\let\pushmacro\localpushmacro
5203\permanent\let\popmacro \localpopmacro
5204
5205%D \macros
5206%D   {setlocalhsize,distributedhsize}
5207%D
5208%D Sometimes we need to work with the \type{ \hsize} that is corrected for
5209%D indentation and left and right skips. The corrected value is available in \type
5210%D {\localhsize}, which needs to be calculated with \type {\setlocalhsize} first. %D
5211%D
5212%D \starttyping
5213%D \setlocalhsize        \hbox to \localhsize{...}
5214%D \setlocalhsize[-1em]  \hbox to \localhsize{...}
5215%D \setlocalhsize[.5ex]  \hbox to \localhsize{...}
5216%D \stoptyping
5217%D
5218%D These examples show us that an optional can be used. The value provided is added
5219%D to \type {\localhsize}.
5220
5221\newdimension\localhsize
5222
5223\permanent\protected\def\setlocalhsize % don't change !
5224  {\doifelsenextoptional
5225     \syst_helpers_set_local_hsize_yes
5226     \syst_helpers_set_local_hsize_nop}
5227
5228\def\syst_helpers_set_local_hsize_nop
5229  {\localhsize\availablehsize}
5230
5231\def\syst_helpers_set_local_hsize_yes[#1]%
5232  {\syst_helpers_set_local_hsize_nop
5233   \advanceby\localhsize#1\relax}
5234
5235\permanent\def\availablehsize
5236  {\dimexpr
5237     \hsize-\leftskip-\rightskip
5238     \ifnum\hangafter<\zerocount
5239       \ifdim\hangindent>\zeropoint-\else+\fi\hangindent
5240     \fi
5241   \relax}
5242
5243\permanent\def\distributedhsize#1#2#3%
5244  {\dimexpr(#1-\numexpr#3-1\relax\dimexpr#2\relax)/#3\relax}
5245
5246\permanent\def\hsizefraction#1#2%
5247  {\dimexpr#1/#2\relax}
5248
5249%D \macros
5250%D   {doifvalue,doifnotvalue,doifelsevalue,
5251%D    doifnothing,doifsomething,doifelsenothing,
5252%D    doifvaluenothing,doifvaluesomething,doifelsevaluenothing}
5253%D
5254%D These \type {\if} commands can be used to access macros (or variables) that are
5255%D normally accessed by using \type {\getvalue}. Using these alternatives safes us
5256%D three tokens per call. Anyone familiar with the not||values ones, can derive
5257%D their meaning from the definitions.
5258
5259\permanent\protected\def\doifvalue#1#2%
5260  {\iftok{\csname#1\endcsname}{#2}%
5261     \expandafter\firstofoneargument
5262   \else
5263     \expandafter\gobbleoneargument
5264   \fi}
5265
5266\permanent\protected\def\doifnotvalue#1#2%
5267  {\iftok{\csname#1\endcsname}{#2}%
5268     \expandafter\gobbleoneargument
5269   \else
5270     \expandafter\firstofoneargument
5271   \fi}
5272
5273\permanent\protected\def\doifelsevalue#1#2%
5274  {\iftok{\csname#1\endcsname}{#2}%
5275     \expandafter\firstoftwoarguments
5276   \else
5277     \expandafter\secondoftwoarguments
5278   \fi}
5279
5280\permanent\protected\def\doifnothing#1%
5281  {\ifempty{#1}%
5282     \expandafter\firstofoneargument
5283   \else
5284     \expandafter\gobbleoneargument
5285   \fi}
5286
5287\permanent\protected\def\doifsomething#1%
5288  {\ifempty{#1}%
5289     \expandafter\gobbleoneargument
5290   \else
5291     \expandafter\firstofoneargument
5292   \fi}
5293
5294\permanent\protected\def\doifelsenothing#1%
5295  {\ifempty{#1}%
5296     \expandafter\firstoftwoarguments
5297   \else
5298     \expandafter\secondoftwoarguments
5299   \fi}
5300
5301\permanent\protected\def\doifelsesomething#1%
5302  {\ifempty{#1}%
5303     \expandafter\secondoftwoarguments
5304   \else
5305     \expandafter\firstoftwoarguments
5306   \fi}
5307
5308\permanent\protected\def\doifvaluenothing#1%
5309  {\ifempty{\csname#1\endcsname}%
5310     \expandafter\firstofoneargument
5311   \else
5312     \expandafter\gobbleoneargument
5313   \fi}
5314
5315\permanent\protected\def\doifvaluesomething#1%
5316  {\ifempty{\csname#1\endcsname}%
5317     \expandafter\gobbleoneargument
5318   \else
5319     \expandafter\firstofoneargument
5320   \fi}
5321
5322\permanent\protected\def\doifelsevaluenothing#1%
5323  {\ifempty{\csname#1\endcsname}%
5324     \expandafter\firstoftwoarguments
5325   \else
5326     \expandafter\secondoftwoarguments
5327   \fi}
5328
5329\aliased\let\doifvalueelse       \doifelsevalue
5330\aliased\let\doifnothingelse     \doifelsenothing
5331\aliased\let\doifsomethingelse   \doifelsesomething
5332\aliased\let\doifvaluenothingelse\doifelsevaluenothing
5333
5334%D \macros
5335%D   {doifemptyelsevalue, doifemptyvalue, doifnotemptyvalue}
5336%D
5337%D Also handy:
5338
5339\permanent\def\doifelseemptyvalue#1%
5340  {\expandafter\ifempty\csname#1\endcsname
5341     \expandafter\firstoftwoarguments
5342   \else
5343     \expandafter\secondoftwoarguments
5344   \fi}
5345
5346\aliased\let\doifemptyvalueelse\doifelseemptyvalue
5347
5348\permanent\def\doifemptyvalue#1%
5349  {\expandafter\ifempty\csname#1\endcsname
5350     \expandafter\firstofoneargument
5351   \else
5352     \expandafter\gobbleoneargument
5353   \fi}
5354
5355\permanent\def\doifnotemptyvalue#1%
5356  {\expandafter\ifempty\csname#1\endcsname
5357     \expandafter\gobbleoneargument
5358   \else
5359     \expandafter\firstofoneargument
5360   \fi}
5361
5362%D \macros
5363%D   {doifallcommonelse}
5364%D
5365%D A complete match of two sets can be tested with \type {\doifallcommonelse}, where
5366%D the first two arguments are sets.
5367
5368\let\syst_helpers_do_common_check_all\gobbleoneargument
5369
5370\def\syst_helpers_do_if_all_common_else#1#2#3#4% slow
5371  {\def\syst_helpers_do_common_check_all##1%
5372     {\ifinset{##1}{#4}\else\donefalse\fi
5373      \ifdone\else\expandafter\quitcommalist\fi}%
5374   \donetrue
5375   \processcommalist[#3]\syst_helpers_do_common_check_all
5376   \ifdone\expandafter#1\else\expandafter#2\fi}
5377
5378\permanent\protected\def\doifelseallcommon{\syst_helpers_do_if_all_common_else\firstoftwoarguments\secondoftwoarguments}
5379\permanent\protected\def\doifallcommon    {\syst_helpers_do_if_all_common_else\firstofoneargument \gobbleoneargument   }
5380\permanent\protected\def\doifnotallcommon {\syst_helpers_do_if_all_common_else\gobbleoneargument  \firstofoneargument  }
5381
5382\aliased\let\doifallcommonelse\doifelseallcommon
5383
5384%D \macros
5385%D   {DOIF,DOIFELSE,DOIFNOT}
5386%D
5387%D \TEX\ is case sensitive. When comparing arguments, this feature sometimes is less
5388%D desirable, for instance when we compare filenames. The next three alternatives
5389%D upcase their arguments before comparing them.
5390%D
5391%D \starttyping
5392%D \DOIF     {string1} {string2} {...}
5393%D \DOIFNOT  {string1} {string2} {...}
5394%D \DOIFELSE {string1} {string2} {then ...}{else ...}
5395%D \stoptyping
5396%D
5397%D We have to use a two||step implementation, because the
5398%D expansion has to take place outside \type{\uppercase}.
5399%D
5400%D These might end up as \LUA based helpers (i.e. consider these
5401%D obsolete:
5402
5403\protected\def\syst_helpers_do_IF#1#2%
5404  {\uppercase{\iftok{#1}{#2}}%
5405     \expandafter\firstofoneargument
5406   \else
5407     \expandafter\gobbleoneargument
5408   \fi}
5409
5410\protected\def\syst_helpers_do_IF_NOT#1#2%
5411  {\uppercase{\iftok{#1}{#2}}%
5412     \expandafter\gobbleoneargument
5413   \else
5414     \expandafter\firstofoneargument
5415   \fi}
5416
5417\protected\def\syst_helpers_do_IF_ELSE#1#2%
5418  {\uppercase{\iftok{#1}{#2}}%
5419     \expandafter\firstoftwoarguments
5420   \else
5421     \expandafter\secondoftwoarguments
5422   \fi}
5423
5424\protected\def\syst_helpers_do_IF_INSTRING_ELSE#1#2%
5425  {\uppercase{\doifelseinstring{#1}{#2}}}
5426
5427\permanent\protected\def\DOIF             #1#2{\normalexpanded{\syst_helpers_do_IF              {#1}{#2}}}% will become obsolete
5428\permanent\protected\def\DOIFNOT          #1#2{\normalexpanded{\syst_helpers_do_IF_NOT          {#1}{#2}}}% will become obsolete
5429\permanent\protected\def\DOIFELSE         #1#2{\normalexpanded{\syst_helpers_do_IF_ELSE         {#1}{#2}}}% will become obsolete
5430\permanent\protected\def\DOIFINSTRINGELSE #1#2{\normalexpanded{\syst_helpers_do_IF_INSTRING_ELSE{#1}{#2}}}% will become obsolete
5431
5432%D \macros
5433%D   {dosingleargumentwithset,
5434%D    dodoubleargumentwithset,dodoubleemptywithset,
5435%D    dotripleargumentwithset,dotripleemptywithset}
5436%D
5437%D These maybe too mysterious macros enable us to handle more than one setup at once.
5438%D
5439%D \starttyping
5440%D \dosingleargumentwithset \command[#1]
5441%D \dodoubleargumentwithset \command[#1][#2]
5442%D \dotripleargumentwithset \command[#1][#2][#3]
5443%D \dodoubleemptywithset    \command[#1][#2]
5444%D \dotripleemptywithset    \command[#1][#2][#3]
5445%D \stoptyping
5446%D
5447%D The first macro calls \type {\command[##1]} for each string in the set~\type
5448%D {#1}. The second one calls for \typ {\command [##1][#2]} and the third, well one
5449%D may guess. These commands support constructions like:
5450%D
5451%D \starttyping
5452%D \def\dodefinesomething[#1][#2]%
5453%D   {\getparameters[\??xx#1][#2]}
5454%D
5455%D \protected\def\definesomething%
5456%D   {\dodoubleargumentwithset\dodefinesomething}
5457%D \stoptyping
5458%D
5459%D Which accepts calls like:
5460%D
5461%D \starttyping
5462%D \definesomething[alfa,beta,...][variable=...,...]
5463%D \stoptyping
5464
5465\let\syst_helpers_with_set_step\relax % maybe push pop
5466
5467\permanent\tolerant\protected\def\dodoubleemptywithset#1#*[#2]#*[#3]%
5468  {\ifempty{#2}\else
5469     \def\syst_helpers_with_set_step##1{#1[##1][#3]}%
5470     \processcommalist[#2]\syst_helpers_with_set_step
5471   \fi}
5472
5473\permanent\tolerant\protected\def\dotripleemptywithset#1#*[#2]#*[#3]#*[#4]%
5474  {\ifempty{#2}\else
5475     \def\syst_helpers_with_set_step##1{#1[##1][#3][#4]}%
5476     \processcommalist[#2]\syst_helpers_with_set_step
5477   \fi}
5478
5479\aliased\let\dodoubleargumentwithset\dodoubleemptywithset
5480\aliased\let\dotripleargumentwithset\dotripleemptywithset
5481
5482%D \macros
5483%D   {stripcharacters,stripspaces}
5484%D
5485%D The next command was needed first when we implemented the \CONTEXT\ interactivity
5486%D macros. When we use labeled destinations, we often cannot use all the characters
5487%D we want. We therefore strip some of the troublemakers, like spaces, from the
5488%D labels before we write them to the \DVI||file, which passes them to for instance
5489%D a \POSTSCRIPT\ file.
5490%D
5491%D \starttyping
5492%D \stripspaces\from\one\to\two
5493%D \stoptyping
5494%D
5495%D Both the old string \type {\one} and the new one \type {\two} are expanded. This
5496%D command is a special case of:
5497%D
5498%D \starttyping
5499%D \stripcharacter\char\from\one\to\two
5500%D \stoptyping
5501%D
5502%D As we can see below, spaces following a control sequence are to enclosed in \type
5503%D {{}}.
5504
5505\let\syst_helpers_strip_character\relax
5506
5507\lettonothing\m_syst_helpers_strip_character
5508
5509\permanent\protected\def\stripcharacter#1\from#2\to#3%
5510  {\def\syst_helpers_strip_character##1#1##2\end
5511     {\edef\m_syst_helpers_strip_character{\m_syst_helpers_strip_character##1}%
5512      \doifnotempty{##2}{\syst_helpers_strip_character##2\end}}%
5513   \lettonothing\m_syst_helpers_strip_character
5514   \edef\m_syst_string_one{#2}%
5515   \expandafter\syst_helpers_strip_character\m_syst_string_one#1\end
5516   \dodoglobal\let#3\m_syst_helpers_strip_character}
5517
5518\permanent\protected\def\stripspaces\from#1\to#2% will become \unspacestring#1\from#2
5519  {\stripcharacter{ }\from#1\to#2}
5520
5521%D \macros
5522%D   {unspacestring}
5523%D
5524%D The next macro does the same but is more compatible with other macros, like \type
5525%D {\convert...}.
5526
5527\permanent\protected\def\unspacestring#1\to#2%
5528  {\stripcharacter{ }\from#1\to#2}
5529
5530%D \macros
5531%D   {executeifdefined}
5532%D
5533%D \CONTEXT\ uses one auxiliary file for all data concerning tables of contents,
5534%D references, two||pass optimizations, sorted lists etc. This file is loaded as
5535%D many times as needed. During such a pass we skip the commands thate are of no use
5536%D at that moment. Because we don't want to come into trouble with undefined
5537%D auxiliary commands, we call the macros in a way similar to \type {\getvalue}. The
5538%D next macro take care of such executions and when not defined, gobbles the
5539%D unwanted arguments.
5540%D
5541%D \starttyping
5542%D \executeifdefined{name}\gobbleoneargument
5543%D \stoptyping
5544%D
5545%D We can of course gobble more arguments using the appropriate gobbling command.
5546
5547\permanent\def\executeifdefined#1% #2 / never change this one again
5548  {\ifcsname#1\endcsname
5549     \expandafter\expandafter\expandafter\lastnamedcs\expandafter\gobbleoneargument
5550   \else
5551     \expandafter\firstofoneargument
5552   \fi}
5553
5554%D This one also has the advantage that it is fully expandable and that it can be
5555%D used after an assignment.
5556
5557%D \macros
5558%D   {doifsomespaceelse}
5559%D
5560%D The next command checks a string on the presence of a space and executed a
5561%D command accordingly.
5562%D
5563%D \starttyping
5564%D \doifsomespaceelse {tekst} {then ...} {else ...}
5565%D \stoptyping
5566%D
5567%D We use this command in \CONTEXT\ for determing if an argument must be broken into
5568%D words when made interactive. Watch the use of \type {\noexpand}.
5569
5570%D Is this one still needed?
5571
5572% \def\syst_helpers_if_some_space_else#1 #2#3^^^^0004{\if\noexpand#2@}
5573%
5574% \def\doifelsesomespace#1%                     % #2#3%
5575%   {\syst_helpers_if_some_space_else#1 @ @^^^^0004% #3\else#2\fi}
5576%      \expandafter\secondoftwoarguments
5577%    \else
5578%      \expandafter\firstoftwoarguments
5579%    \fi}
5580
5581\permanent\edef\doifelsesomespace#1%
5582  {\noexpand\ifhastok\space{#1}%
5583     \noexpand\expandafter\noexpand\firstoftwoarguments
5584   \noexpand\else
5585     \noexpand\expandafter\noexpand\secondoftwoarguments
5586   \noexpand\fi}
5587
5588\aliased\let\doifsomespaceelse\doifelsesomespace
5589
5590%D \macros
5591%D   {processseparatedlist}
5592%D
5593%D Maybe a bit late, but here is a more general version of the \type
5594%D {\processcommalist} command. This time we don't handle nesting but accept
5595%D arbitrary seperators.
5596%D
5597%D \starttyping
5598%D \processseparatedlist[list][separator]\command
5599%D \stoptyping
5600%D
5601%D One can think of things like:
5602%D
5603%D \starttyping
5604%D \processseparatedlist[alfa+beta+gamma][+]\message
5605%D \stoptyping
5606%D
5607%D We want to handle all situations, like:
5608%D
5609%D \startbuffer
5610%D \processseparatedlist[{aap noot}]  [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii}
5611%D \processseparatedlist[{aap} {noot}][ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii}
5612%D \processseparatedlist[aap {noot}]  [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii}
5613%D \processseparatedlist[aap noot]    [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii}
5614%D \stopbuffer
5615%D
5616%D \typebuffer \getbuffer
5617%D
5618%D Therefore we smuggle a \type {\relax} in front of the argument, which we remove
5619%D afterwards.
5620
5621\let\syst_helpers_process_separated_list_step\relax
5622
5623% \def\syst_helpers_process_separated_list#1]#*[#2]#3%
5624%    {\def\syst_helpers_process_separated_list_step##1##2#2%
5625%       {\def\m_syst_string_one{##2}% suggested by VZ
5626%        \if]##1%
5627%          \let\syst_helpers_process_separated_list_step\relax
5628%        \orelse\ifx\blankspace\m_syst_string_one
5629%          #3{##1}%
5630%        \orelse\if]##2%
5631%          \let\syst_helpers_process_separated_list_step\relax
5632%        \else
5633%          #3{##1##2}%
5634%        \fi
5635%        \syst_helpers_process_separated_list_step}%
5636%     \expandafter\syst_helpers_process_separated_list_step\gobbleoneargument#1#2]#2}
5637
5638% \def\syst_helpers_process_separated_list#1]#*[#2]#3%
5639%    {\def\syst_helpers_process_separated_list_step##1##2#2%
5640%       {\def\m_syst_string_one{##2}% suggested by VZ
5641%        \if]##1%
5642%        \orelse\ifx\blankspace\m_syst_string_one
5643%          #3{##1}%
5644%          \expandafter\syst_helpers_process_separated_list_step
5645%        \orelse\if]##2%
5646%        \else
5647%          #3{##1##2}%
5648%          \expandafter\syst_helpers_process_separated_list_step
5649%        \fi
5650%        }%
5651%     \expandafter\syst_helpers_process_separated_list_step\gobbleoneargument#1#2]#2}
5652
5653% \permanent\protected\def\processseparatedlist[%
5654%   {\syst_helpers_process_separated_list\relax}
5655
5656\permanent\protected\def\processseparatedlist[#+]#*[#2]#3%
5657   {\tolerant\def\syst_helpers_process_separated_list_step##1#2%
5658      {\ifarguments\or
5659         #3{##1}%
5660         \expandafter\syst_helpers_process_separated_list_step
5661       \fi}%
5662    \syst_helpers_process_separated_list_step#1\ignorearguments\ignorearguments}
5663
5664%D \macros
5665%D   {processlist}
5666%D
5667%D An even more general list processing macro is the following one:
5668%D
5669%D \starttyping
5670%D \processlist{beginsym}{endsym}{separator}\docommand list
5671%D \stoptyping
5672%D
5673%D This one supports arbitrary open and close symbols as well as user defined
5674%D separators.
5675%D
5676%D \starttyping
5677%D \processlist(){=>}\docommand(a=>b=>c=>d)
5678%D \stoptyping
5679
5680\let\syst_helpers_process_any_list       \relax
5681\let\syst_helpers_process_any_list_indeed\relax
5682\let\syst_helpers_process_any_list_step  \relax
5683
5684\permanent\protected\def\processlist#1#2#3#4% no blank skipping !
5685  {\def\syst_helpers_process_any_list_indeed##1#2%
5686     {\def\syst_helpers_process_any_list_step####1####2#3%
5687        {\ifx#2####1%
5688           \let\syst_helpers_process_any_list_step\relax
5689         \orelse\ifx#2####2%
5690           \let\syst_helpers_process_any_list_step\relax
5691         \else
5692           #4{####1####2}%
5693         \fi
5694         \syst_helpers_process_any_list_step}%
5695      \expandafter\syst_helpers_process_any_list_step\gobbleoneargument##1#3#2#3}%
5696   \def\syst_helpers_process_any_list#1%
5697     {\syst_helpers_process_any_list_indeed\relax}%
5698    \syst_helpers_process_any_list}
5699
5700%D \macros
5701%D   {processassignlist}
5702%D
5703%D Is possible to combine an assignment list with one containing keywords.
5704%D Assignments are treated accordingly, keywords are treated by \type {\command}.
5705%D
5706%D \starttyping
5707%D \processassignlist[...=...,...=...,...]\commando
5708%D \stoptyping
5709%D
5710%D This command can be integrated in \type {\getparameters}, but we decided best not
5711%D to do so.
5712
5713\let\syst_helpers_process_assign_list_assign\gobbleoneoptional
5714\let\syst_helpers_process_assign_list_step  \gobbleoneargument
5715
5716\permanent\protected\def\processassignlist#1[#2]#3%
5717  {\def\syst_helpers_process_assign_list_assign[##1=##-=##2]%
5718     {\iftok{##2}{\relax}#3{##1}\fi}%
5719   \def\syst_helpers_process_assign_list_step##1%
5720     {\syst_helpers_process_assign_list_assign[##1==\relax]}%
5721   \processcommalist[#2]\syst_helpers_process_assign_list_step}
5722
5723%D \macros
5724%D   {untextargument
5725%D    untexcommand}
5726%D
5727%D When manipulating data(bases) and for instance generating index entries, the next
5728%D three macros can be of help:
5729%D
5730%D \starttyping
5731%D \untextargument{...}\to\name
5732%D \untexcommand  {...}\to\name
5733%D \stoptyping
5734%D
5735%D They remove braces and backslashes and give us something to sort.
5736
5737\lettonothing\m_syst_helpers_untexed
5738
5739\permanent\protected\def\untexsomething
5740  {\begingroup
5741   \catcode\leftbraceasciicode \ignorecatcode
5742   \catcode\rightbraceasciicode\ignorecatcode
5743   \escapechar\minusone
5744   \syst_helpers_untex_something}
5745
5746\def\syst_helpers_untex_something#1#2\to#3%
5747  {\doglobal#1#2\to\m_syst_helpers_untexed
5748   \endgroup
5749   \let#3\m_syst_helpers_untexed}
5750
5751\permanent\protected\def\untexargument{\untexsomething\convertargument}
5752\permanent\protected\def\untexcommand {\untexsomething\convertcommand}
5753
5754%D \macros
5755%D   {ScaledPointsToBigPoints,ScaledPointsToWholeBigPoints}
5756%D
5757%D One characteristic of \POSTSCRIPT\ and \PDF\ is that both used big points (\TEX's
5758%D bp). The next macros convert points and scaled points into big points. The magic
5759%D factor $72/72.27$ can be found in most \TEX\ related books.
5760%D
5761%D \starttyping
5762%D \ScaledPointsToBigPoints      {number} \target
5763%D \ScaledPointsToWholeBigPoints {number} \target
5764%D \stoptyping
5765
5766\aliased\let\tobigpoints     \clf_tobigpoints      % todo: permanent at lua end
5767\aliased\let\towholebigpoints\clf_towholebigpoints % todo: permanent at lua end
5768
5769\permanent\protected\def\PointsToBigPoints           #1#2{\edef#2{\tobigpoints     #1}}             % can be avoided
5770\permanent\protected\def\PointsToWholeBigPoints      #1#2{\edef#2{\towholebigpoints#1}}             % can be avoided
5771\permanent\protected\def\ScaledPointsToBigPoints     #1#2{\edef#2{\tobigpoints     #1\scaledpoint}} % obsolete
5772\permanent\protected\def\ScaledPointsToWholeBigPoints#1#2{\edef#2{\towholebigpoints#1\scaledpoint}} % obsolete
5773
5774%D \macros
5775%D   {PointsToReal}
5776%D
5777%D Points can be stripped from their suffix by using \type {\withoutpt}. The next
5778%D macro enveloppes this macro.
5779%D
5780%D \starttyping
5781%D \PointsToReal {dimension} \target
5782%D \stoptyping
5783
5784% \permanent\protected\def\PointsToReal#1#2{\edef#2{\thewithoutunit\dimexpr#1}}
5785
5786\permanent\protected\def\PointsToReal#1#2{\edef#2{\toscaled#1}}
5787
5788%D \macros
5789%D  {dontleavehmode}
5790%D
5791%D Sometimes when we enter a paragraph with some command, the first token gets the
5792%D whole first line. We can prevent this by saying:
5793%D
5794%D \starttyping
5795%D \dontleavehmode
5796%D \stoptyping
5797%D
5798%D This command is used in for instance the language module \type {lang-ini}. The
5799%D first version was:
5800%D
5801%D \starttyping
5802%D \def\dontleavehmode{\ifhmode\orelse\ifmmode\else$ $\fi}
5803%D \stoptyping
5804%D
5805%D Next, Taco came with a better alternative (using mathsurround):
5806%D
5807%D \starttyping
5808%D \def\dontleavehmode
5809%D   {\ifhmode\orelse\ifmmode\else
5810%D      {\mathsurround\zeropoint\everymath\emptytoks$ $}%
5811%D    \fi}
5812%D \stoptyping
5813%D
5814%D And finaly we got the following alternative, one that avoids interfering grouping
5815%D at the cost of a box.
5816%D
5817%D \starttyping
5818%D \newbox\b_syst_helpers_dlh
5819%D
5820%D \protected\def\dontleavehmode
5821%D   {\ifhmode\orelse\ifmmode\else
5822%D      \setbox\b_syst_helpers_dlh\hbox{\mathsurround\zeropoint\everymath\emptytoks$ $}\unhbox\b_syst_helpers_dlh
5823%D    \fi}
5824%D \stoptyping
5825%D
5826%D But, as we run a recent version of \TEX, we can use the new primitive:
5827
5828\aliased\let\dontleavehmode\quitvmode
5829
5830%D \macros
5831%D   {utfupper, utflower, uppercasestring, lowercasestring}
5832%D
5833%D The names tell what they do:
5834%D
5835%D \starttyping
5836%D \uppercasestring somestring\to\somestring
5837%D \lowercasestring somestring\to\somestring
5838%D \stoptyping
5839%D
5840%D The first argument may be a \type{\macro}.
5841%D
5842%D These macros are sort of obsolete as we never use uppercase this way. But
5843%D nevertheless we provide them:
5844
5845\permanent\def\utfupper#1{\clf_upper{#1}} % expandable
5846\permanent\def\utflower#1{\clf_lower{#1}} % expandable
5847
5848\permanent\protected\def\uppercasestring#1\to#2{\dodoglobal\edef#2{\clf_upper{#1}}}
5849\permanent\protected\def\lowercasestring#1\to#2{\dodoglobal\edef#2{\clf_lower{#1}}}
5850
5851%D \macros
5852%D   {handletokens}
5853%D
5854%D With the next macro we enter a critical area of macro expansion. What we want is
5855%D a macro that looks like:
5856%D
5857%D \starttyping
5858%D \handletokens some tokens\with \somemacro
5859%D \stoptyping
5860%D
5861%D A bonus example:
5862%D
5863%D \starttyping
5864%D \hbox{\handletokens tekst en meer tekst\with\ruledhbox}
5865%D
5866%D \def\weetikveel#1{\if#1\blankspace\space\else\ruledhbox{#1}\fi}
5867%D
5868%D \hbox{\handletokens tekst en meer tekst\with\weetikveel}
5869%D \stoptyping
5870
5871%D \macros
5872%D   {counttoken,counttokens}
5873%D
5874%D For the few occasions that we want to know the number of specific tokens in a
5875%D string, we can use:
5876%D
5877%D \starttyping
5878%D \counttoken  token\in string\to \somecount
5879%D \counttokens          string\to \somecount
5880%D \stoptyping
5881%D
5882%D This macro, that for instance is used in \type {cont-tab}, takes a real counter.
5883%D The macro can be preceded by \type {\doglobal}.
5884
5885\def\syst_helpers_count_token#1% obeys {}
5886  {\def\m_syst_string_three{#1}%
5887   \ifx\m_syst_string_two\m_syst_string_three \else
5888     \ifx\m_syst_string_one\m_syst_string_three
5889       \advanceby\privatescratchcounter\plusone
5890     \fi
5891     \expandafter\syst_helpers_count_token
5892   \fi}
5893
5894\permanent\protected\def\counttoken#1\in#2\to#3%
5895  {\privatescratchcounter\zerocount
5896   \def\m_syst_string_one{#1}%
5897   \def\m_syst_string_two{\end}%
5898   \syst_helpers_count_token#2\end
5899   \dodoglobal#3\privatescratchcounter}
5900
5901\permanent\protected\def\counttokens#1\to#2%
5902  {\privatescratchcounter\zerocount
5903   \def\syst_helpers_count_token##1{\advanceby\privatescratchcounter\plusone}%
5904   \handletokens#1\with\syst_helpers_count_token
5905   \dodoglobal#2\privatescratchcounter}
5906
5907%D \macros
5908%D   {splitofftokens}
5909%D
5910%D Running this one not always gives the expected results. Consider for instance the
5911%D macro for which I originally wrote this token handler.
5912
5913\let\syst_helpers_split_off_tokens\gobbleoneargument
5914
5915\permanent\protected\def\splitofftokens#1\from#2\to#3% slow but hardly used
5916  {\ifnum#1>\zerocount
5917     \privatescratchcounter#1\relax
5918     \def\syst_helpers_split_off_tokens##1%
5919       {\ifnum\privatescratchcounter>\zerocount
5920          \advanceby\privatescratchcounter \minusone
5921          \edef#3{#3##1}%
5922        \fi}%
5923   % \lettonothing#3% #3 can be #2, so:
5924     \expandafter\let\expandafter#3\expandafter\empty
5925     \expandafter\handletokens#2\with\syst_helpers_split_off_tokens
5926   \else
5927     \edef#3{#2}%
5928   \fi}
5929
5930%D This macro can be called like:
5931%D
5932%D \startbuffer[example]
5933%D \splitofftokens10\from01234567 890123456789\to\test [\test]
5934%D \stopbuffer
5935%D
5936%D up there. The reason for this is not that logical but follows from \TEX's
5937%D However, the characters that we expect to find in \type {\test} just don't show
5938%D sometimes mysterious way of expanding. Look at this:
5939%D
5940%D \startbuffer[next]
5941%D \def\next{a} \edef\test{\next}                                          [\test]
5942%D \let\next=b  \edef\test{\test\next}                                     [\test]
5943%D \let\next=c  \edef\test{\next}                                          [\test]
5944%D \let\next=d  \edef\test{\test\next}                                     [\test]
5945%D \let\next=e  \expandafter\edef\expandafter\test\expandafter{\test\next} [\test]
5946%D \stopbuffer
5947%D
5948%D \typebuffer[next]
5949%D
5950%D Careful reading shows that inside an \type {\edef} macro's that are \type {\let}
5951%D are not expanded!
5952%D
5953%D \unprotect\getbuffer[next]\protect
5954%D
5955%D That's why we finally end up with a macro that looks ahead by using an
5956%D assignment, this time by using \type {\futurelet}, and grabbing an argument as
5957%D well. That way we can handle the sentinal, a blank space and grouped tokens.
5958
5959\mutable\lettonothing\nexthandledtoken % part of public interface
5960
5961\let\syst_helpers_handle_tokens_command\relax
5962
5963\protected\def\syst_helpers_handle_tokens
5964  {\futurelet\nexthandledtoken\syst_helpers_handle_tokens_indeed}
5965
5966\permanent\protected\def\handletokens#1\with#2%
5967  {\gdef\syst_helpers_handle_tokens_command{#2}% permits more complex #2's
5968   \syst_helpers_handle_tokens#1\end}
5969
5970\def\syst_helpers_handle_tokens_indeed
5971  {\ifx\nexthandledtoken\blankspace
5972     \expandafter\syst_helpers_handle_tokens_indeed_one
5973   \orelse\ifx\nexthandledtoken\end
5974     \expandafter\gobbletwoarguments % also gobble the \end
5975   \else
5976     \expandafter\syst_helpers_handle_tokens_indeed_two
5977   \fi *}
5978
5979\def\syst_helpers_handle_tokens_indeed_one * %
5980  {\syst_helpers_handle_tokens_command{ }\syst_helpers_handle_tokens}
5981
5982\def\syst_helpers_handle_tokens_indeed_two *#1%
5983  {\syst_helpers_handle_tokens_command{#1}\syst_helpers_handle_tokens}
5984
5985%D This macro is tested on:
5986%D
5987%D \def\xxx#1{[#1]}
5988%D
5989%D \startlines
5990%D \handletokens         abc\with\xxx
5991%D \handletokens       a b c\with\xxx
5992%D \handletokens     a  b  c\with\xxx
5993%D \handletokens      a{bc}d\with\xxx
5994%D \handletokens a\space bc \with\xxx
5995%D \stoplines
5996%D
5997%D And our previous example shows up as:
5998%D
5999%D \getbuffer[example]
6000
6001%D \macros
6002%D   {iftrialtypesetting, ifvisible}
6003%D
6004%D The next boolean is at first sight a strange one. Sometimes one does a trial
6005%D typesetting run, for instance to determine dimensions. Some mechanisms, like
6006%D object inclusion, can fail on such trials. Temporary setting the next boolean to
6007%D true, helps a lot. The second boolena can be used to inhibit processing
6008%D completely.
6009
6010\newif\ifvisible \visibletrue
6011
6012\newtoks\everysettrialtypesetting
6013\newtoks\everyresettrialtypesetting
6014
6015\permanent\protected\def\settrialtypesetting  {\expand\everysettrialtypesetting  } % obeys grouping so
6016\permanent\protected\def\resettrialtypesetting{\expand\everyresettrialtypesetting} % this one is seldom needed
6017
6018\aliased\let\iftrialtypesetting\iffalse % so we have no \trialtypesettingtrue|false in mkiv !
6019
6020\appendtoks \enforced\aliased\let\iftrialtypesetting\iftrue  \to \everysettrialtypesetting
6021\appendtoks \enforced\aliased\let\iftrialtypesetting\iffalse \to \everyresettrialtypesetting
6022
6023%D \macros
6024%D   {twodigitrounding}
6025%D
6026%D The next macro rounds a real number to two digits. They are probably no longer needed
6027%D but we keep them around for a while.
6028
6029\permanent\def\integerrounding   #1{\clf_rounded\zerocount\numexpr#1\relax}
6030\permanent\def\onedigitrounding  #1{\clf_rounded\plusone  \numexpr#1\relax}
6031\permanent\def\twodigitrounding  #1{\clf_rounded\plustwo  \numexpr#1\relax}
6032\permanent\def\threedigitrounding#1{\clf_rounded\plusthree\numexpr#1\relax}
6033
6034%D \macros
6035%D   {processcontent}
6036%D
6037%D This macro is first used in the tabulation macros.
6038%D
6039%D \starttyping
6040%D \protected\def\starthans%
6041%D   {\processcontent{stophans}\test{\message{\test}\wait}}
6042%D \stoptyping
6043
6044% \starttabulate[|||]
6045%     \NC \type{#} \NC # \NC \NR
6046% \stoptabulate
6047%
6048% \def\test#1%
6049%   {\starttabulate[|||]
6050%      \NC \type{#1} \NC #1 \NC \NR
6051%    \stoptabulate}
6052%
6053% \test{!}
6054
6055%% \permanent\protected\def\processcontent#1%
6056%%   {\begingroup\expandafter\syst_helpers_process_content\csname#1\endcsname}
6057
6058\protected\def\syst_helpers_process_content#1#2#3%
6059  {\protected\def\syst_helpers_process_content##1#1%
6060     {\endgroup\def#2{##1}#3}%
6061   \syst_helpers_process_content}
6062
6063\permanent\protected\def\processcontent#1%
6064  {\begingroup
6065   \catcode\hashasciicode\othercatcode
6066   \expandafter\syst_helpers_process_content\csname#1\endcsname}
6067
6068%D \macros
6069%D   {dogobblesingleempty, dogobbledoubleempty}
6070%D
6071%D These two macros savely grab and dispose two arguments. We also have a few
6072%D private ones defined earlier.
6073
6074\permanent\tolerant\protected\def\dogobbledoubleempty[#S#-]#*[#S#-]{}
6075\permanent\tolerant\protected\def\dogobblesingleempty        [#S#-]{}
6076
6077\aliased\let\gobblesingleempty\dogobblesingleempty % also used
6078\aliased\let\gobbledoubleempty\dogobbledoubleempty % also used
6079
6080%D \macros
6081%D   {setdimensionwithunit, freezedimensionwithunit}
6082%D
6083%D The next assignments are all valid:
6084%D
6085%D \starttyping
6086%D \setdimensionwithunit\scratchdimen{10}  {cm}
6087%D \setdimensionwithunit\scratchdimen{10cm}{cm}
6088%D \setdimensionwithunit\scratchdimen{10cm}{}
6089%D \freezedimensionwithunit\SomeWidth{\textwidth}
6090%D \freezedimensionwithunit\SomeDepth{\strutdp}
6091%D \stoptyping
6092%D
6093%D As an alternative for the next macro we can use a global assignment inside a box.
6094%D The \type {\empty}'s permits gobbling while preventing spurious \type {\relax}'s.
6095
6096\permanent\protected\def\setdimensionwithunit#1#2#3% number unit dimension / nice trick
6097  {\afterassignment\gobblefourarguments#1=#2#3pt\relax\empty\empty\empty\empty}
6098
6099\permanent\protected\def\freezedimensionwithunit#1#2%
6100  {\setdimensionwithunit\privatescratchdimen#1{#2}\edef#1{\the\privatescratchdimen}}
6101
6102%D \macros
6103%D   {doifsometokselse, doifsometoks}
6104%D
6105%D Not that fast I guess, but here's a way to test for token registers being empty.
6106
6107\permanent\protected\def\doifelsesometoks#1%
6108  {\iftok#1\emptytoks
6109     \expandafter\secondoftwoarguments
6110   \else
6111     \expandafter\firstoftwoarguments
6112   \fi}
6113
6114\permanent\protected\def\doifsometoks#1%
6115  {\iftok#1\emptytoks
6116     \expandafter\gobbleoneargument
6117   \else
6118     \expandafter\firstofoneargument
6119   \fi}
6120
6121\permanent\protected\def\doifemptytoks#1%
6122  {\iftok#1\emptytoks
6123     \expandafter\firstofoneargument
6124   \else
6125     \expandafter\gobbleoneargument
6126   \fi}
6127
6128\aliased\let\doifsometokselse\doifelsesometoks
6129
6130%D \macros
6131%D   {startstrictinspectnextcharacter}
6132%D
6133%D This one is for the bibliography module (still?):
6134
6135\let\syst_helpers_strict_inspect_next_character[
6136
6137\def\syst_helpers_strict_inspect_next_character% no user macro !
6138  {\ifx\nexttoken[%
6139     \expandafter\m_syst_action_yes
6140   \else
6141     \expandafter\m_syst_action_nop
6142   \fi}
6143
6144\permanent\protected\def\strictdoifelsenextoptional#1#2%
6145  {\def\m_syst_action_yes{#1}%
6146   \def\m_syst_action_nop{#2}%
6147   \futurelet\nexttoken\syst_helpers_strict_inspect_next_character}
6148
6149\aliased\let\strictdoifnextoptionalelse\strictdoifelsenextoptional
6150
6151%D \macros
6152%D   {gobblespacetokens}
6153%D
6154%D This macro needs a speed-up!
6155
6156%\def\gobblespacetokens
6157%  {\doifnextcharelse\empty\donothing\donothing} % no {}\do\do !
6158
6159\permanent\def\gobblespacetokens
6160  {\afterassignment\nexttoken\let\nexttoken=}
6161
6162%D \macros
6163%D   {verbatimargument}
6164%D
6165%D As the name says, this macro converts its argument to a (rather safe) string.
6166
6167\aliased\let\verbatimstring\detokenize
6168
6169%D These are needed in ordinal number conversions:
6170
6171\permanent\def\lastdigit#1%
6172  {\expandafter\thelastdigit\number#1\relax}
6173
6174\permanent\def\thelastdigit#1#2%
6175  {\ifrelax#2#1\else\expandafter\thelastdigit\expandafter#2\fi}
6176
6177\permanent\def\lasttwodigits#1%
6178  {\expandafter\thelasttwodigits\expandafter0\number#1\relax}
6179
6180\permanent\def\thelasttwodigits#1#2#3% 0 dig ... \relax
6181  {\ifrelax#3#1#2\else\expandafter\thelasttwodigits\expandafter#2\expandafter#3\fi}
6182
6183%D \macros
6184%D   {serializecommalist}
6185%D
6186%D Concatenate commalists:
6187
6188\let\syst_helpers_serialize_comma_list_step\relax
6189
6190\mutable\lettonothing\serializedcommalist
6191
6192\def\syst_helpers_serialize_comma_list_step#1%
6193  {\edef\serializedcommalist{\serializedcommalist#1}}
6194
6195\permanent\protected\def\serializecommalist[#1]%
6196  {\lettonothing\serializedcommalist
6197   \processcommacommand[#1]\syst_helpers_serialize_comma_list_step}
6198
6199%D \macros
6200%D   {purenumber}
6201%D
6202%D Sometimes we need control over when \TEX\ stops reading a number, especially in
6203%D full expandable macros where using \type {\relax} would lead to disasters.
6204%D
6205%D \starttyping
6206%D \ifodd\purenumber{...}\space ... \else ... \fi
6207%D \stoptyping
6208%D
6209%D Here we use a space as number delimiter in combination with a space- and
6210%D relax-less \type {\purenumber}. This macro works ok with \type {\the}, \type
6211%D {\number} as well as \ETEX's \type {\numexpr}.
6212
6213\permanent\def\purenumber#1{\expandafter\firstofoneargument\expandafter{\number#1}}
6214
6215%D \macros
6216%D   {filterfromvalue, filterfromnext}
6217%D
6218%D \starttyping
6219%D \setvalue{xx}{{A}{B}{C}}
6220%D
6221%D \filterfromvalue{xx}{3}{3}
6222%D \filterfromvalue{xx}{3}{2}
6223%D \filterfromvalue{xx}{3}{1}
6224%D \stoptyping
6225
6226% can be sped up with ignored arguments
6227
6228\def\syst_filter_from_next_yes#1%
6229  {\advanceby\scratchcounterone\plusone
6230   \ifnum\scratchcounterone=\scratchcountertwo
6231     \scratchtoks{#1}%
6232   \fi
6233   \ifnum\scratchcounterone=\scratchcounterthree
6234     \expandafter\syst_filter_from_next_nop
6235   \else
6236     \expandafter\syst_filter_from_next_yes
6237   \fi}
6238
6239\def\syst_filter_from_next_nop
6240  {\expandafter\endgroup
6241   \expandafter\endlocalcontrol
6242   \the\scratchtoks}
6243
6244\permanent\def\filterfromnext#1#2% max n {..}{..}{..}{..}
6245  {\beginlocalcontrol
6246   \begingroup
6247   \scratchcounterone   \zerocount
6248   \scratchcountertwo   #2%
6249   \scratchcounterthree #1%
6250   \syst_filter_from_next_yes}
6251
6252\permanent\def\filterfromvalue#1#2#3% max n {..}{..}{..}{..}
6253  {\beginlocalcontrol
6254   \begingroup
6255   \scratchcounterone   \zerocount
6256   \scratchcountertwo   #3%
6257   \scratchcounterthree #2%
6258   \expandafter\expandafter\expandafter\syst_filter_from_next_yes\csname#1\endcsname}
6259
6260%D \macros
6261%D   {definemeasure}
6262%D
6263%D \starttyping
6264%D \definemeasure[mywidth][\dimexpr(\textwidth-1cm)]
6265%D
6266%D ... \measure{mywidth} ...
6267%D \stoptyping
6268
6269\installsystemnamespace{measure}
6270
6271\permanent\tolerant\protected\def\definemeasure[#1]#*[#2]{\defcsname \??measure#1\endcsname{#2}}
6272\permanent\tolerant\protected\def\freezemeasure[#1]#*[#2]{\edefcsname\??measure#1\endcsname{\the\dimexpr#2}}
6273
6274\permanent\protected\def\setmeasure #1#2{\defcsname \??measure#1\endcsname{#2}}             % quick way
6275\permanent\protected\def\setgmeasure#1#2{\gdefcsname\??measure#1\endcsname{#2}}             % quick way
6276\permanent\protected\def\setemeasure#1#2{\edefcsname\??measure#1\endcsname{\the\dimexpr#2}} % quick way
6277\permanent\protected\def\setxmeasure#1#2{\xdefcsname\??measure#1\endcsname{\the\dimexpr#2}} % quick way
6278
6279\permanent\def\measure   {\the\measured}
6280\permanent\def\measured#1{\dimexpr\ifcsname\??measure#1\endcsname\lastnamedcs\else\zeropoint\fi\relax}
6281
6282% #2 could be omitted, but we want to support spaces
6283%
6284% \setmeasure {x}  {1cm}
6285% \setmeasure {xx} {1cm}
6286% \setmeasure {xxx}{1cm}
6287
6288%D \macros
6289%D   {definequantity}
6290%D
6291%D These do the same but for numbers.
6292
6293\installsystemnamespace{quantity}
6294
6295\permanent\tolerant\protected\def\definequantity[#1]#*[#2]{\defcsname \??quantity#1\endcsname{#2}}
6296\permanent\tolerant\protected\def\freezequantity[#1]#*[#2]{\edefcsname\??quantity#1\endcsname{\the\numexpr#2}}
6297
6298\permanent\protected\def\setquantity #1#2{\defcsname \??quantity#1\endcsname{#2}}             % quick way
6299\permanent\protected\def\setgquantity#1#2{\gdefcsname\??quantity#1\endcsname{#2}}             % quick way
6300\permanent\protected\def\setequantity#1#2{\edefcsname\??quantity#1\endcsname{\the\numexpr#2}} % quick way
6301\permanent\protected\def\setxquantity#1#2{\xdefcsname\??quantity#1\endcsname{\the\numexpr#2}} % quick way
6302
6303\permanent\def\quantity        {\the\quantitied}
6304\permanent\def\quantitied    #1{\numexpr\ifcsname\??quantity#1\endcsname\lastnamedcs\else\zeropoint\fi\relax}
6305\permanent\def\directquantity#1{\the\numexpr#1\relax}
6306
6307% let\quantified\quantitied
6308
6309%D \macros
6310%D   {dividedsize}
6311%D
6312%D This one can be used inside a measure (used in m4all):
6313%D
6314%D \starttyping
6315%D \definemeasure[columnwidth][\dividebydsize\textwidth{1em}{3}]
6316%D \stoptyping
6317
6318\permanent\def\dividebydsize#1#2#3% size gap n
6319  {\dimexpr
6320     \ifnum\dimexpr#1\relax>\plusone
6321       (\dimexpr#1\relax-\numexpr#3-\plusone\relax\dimexpr#2\relax)/#3\else#1%
6322     \fi
6323   \relax}
6324
6325%D \macros
6326%D   {doifdimensionelse}
6327%D
6328%D This is a dirty one: we simply append a unit and discard it when needed.
6329
6330\permanent\def\doifelsedimension#1%
6331  {\ifchkdim#1\or
6332     \expandafter\firstoftwoarguments
6333   \else
6334     \expandafter\secondoftwoarguments
6335   \fi}
6336
6337\aliased\let\doifdimensionelse\doifelsedimension
6338
6339%D Ok, here's another one, slower but seldom used. This one scans the text.
6340%D
6341%D \starttabulate[|Tc|Tc|]
6342%D \NC pt      \NC  \doifdimenstringelse     {pt}{yes}{no} \NC \NR
6343%D \NC 12pt    \NC  \doifdimenstringelse  {-12pt}{yes}{no} \NC \NR
6344%D \NC 1pt     \NC  \doifdimenstringelse    {1pt}{yes}{no} \NC \NR
6345%D \NC 12pt    \NC  \doifdimenstringelse   {12pt}{yes}{no} \NC \NR
6346%D \NC 12.0pt  \NC  \doifdimenstringelse {12.0pt}{yes}{no} \NC \NR
6347%D \NC -.12pt  \NC  \doifdimenstringelse {-.12pt}{yes}{no} \NC \NR
6348%D \NC .12pt   \NC  \doifdimenstringelse  {.12pt}{yes}{no} \NC \NR
6349%D \NC -12pt   \NC  \doifdimenstringelse  {-12pt}{yes}{no} \NC \NR
6350%D \NC -12.0pt \NC  \doifdimenstringelse{-12.0pt}{yes}{no} \NC \NR
6351%D \NC big     \NC  \doifdimenstringelse    {big}{yes}{no} \NC \NR
6352%D \NC 10      \NC  \doifdimenstringelse     {10}{yes}{no} \NC \NR
6353%D \NC 1       \NC  \doifdimenstringelse      {1}{yes}{no} \NC \NR
6354%D \stoptabulate
6355
6356\aliased\let\doifelsedimenstring\doifelsedimension
6357\aliased\let\doifdimenstringelse\doifelsedimenstring
6358
6359%D \macros
6360%D   {comparedimension,comparedimensioneps}
6361%D
6362%D We no longer use the \MKIV\ dirty trick. These are obsolete anyway.
6363
6364\newdimension\roundingeps \roundingeps=10sp
6365\newconstant \compresult
6366
6367\permanent\protected\def\comparedimension#1#2%
6368  {\compresult
6369     \ifcmpdim#1#2 % space wil be skipped
6370       \zerocount
6371     \or
6372       \plusone
6373     \else
6374       \plustwo
6375     \fi}
6376
6377\permanent\protected\def\comparedimensioneps#1#2% todo: use eps feature
6378  {\compresult
6379     \ifabsdim\dimexpr#1-#2\relax<\roundingeps
6380       \zerocount
6381     \orelse\ifdim#1<#2 % space wil be skipped
6382       \plusone
6383     \else
6384       \plustwo
6385     \fi}
6386
6387% pretty ugly but fast
6388
6389% \copycsname xxx\endcsname\csname ..\endcsname
6390
6391\permanent\protected\def\copycsname{\expandafter\expandafter\expandafter\let\expandafter\expandafter\csname}
6392
6393% \letcscsname    \crap               \csname ..\endcsname
6394% \letcsnamecs    \csname ..\endcsname\crap
6395% \letcsnamecsname\csname ..\endcsname\csname ..\endcsname
6396
6397\permanent\protected\def\letcscsname    {\expandafter\let\expandafter}
6398\permanent\protected\def\letcsnamecs    {\expandafter\let}
6399\permanent\protected\def\letcsnamecsname{\expandafter\expandafter\expandafter\let\expandafter\expandafter}
6400
6401% another one, add an item to a commalist
6402
6403\permanent\protected\def\addvalue#1#2% cs item
6404  {\ifcsname#1\endcsname\else\letcsname#1\endcsname\empty\fi
6405   \normalexpanded{\addtocommalist{#2}\expandafter\noexpand\csname#1\endcsname}}
6406
6407%D Are these ever used? Anyway, these variants are somewhat more efficient than
6408%D the \MKIV variants for larger strings.
6409
6410% A variant:
6411%
6412% \permanent\def\unspaced#1%
6413%   {\localcontrolled{\begingroup\catcode\spaceasciicode\ignorecatcode}%
6414%    \tokenized{#1}%
6415%    \localcontrolled{\endgroup}}
6416%
6417% but we can also do this:
6418
6419\permanent\def\unspaced#1%
6420  {\tokenized \s!catcodetable \ctdcatcodes {#1}}
6421
6422\permanent\protected\def\unspaceargument#1\to#2%
6423  {\edef#2{\tokenized \s!catcodetable \ctdcatcodes {#1}}}
6424
6425\permanent\protected\def\unspaceafter#1#2%
6426  {\expandafter#1\expandafter{\tokenized \s!catcodetable \ctdcatcodes {#2}}}
6427
6428\permanent\protected\def\doifelsehasspace#1%
6429  {\expandafter\ifhastok\space{#1}%
6430     \expandafter\firstoftwoarguments
6431   \else
6432     \expandafter\secondoftwoarguments
6433   \fi}
6434
6435\aliased\let\doifhasspaceelse\doifelsehasspace
6436
6437% this will replace loadfile once and alike !!! todo
6438
6439\installsystemnamespace{flag}
6440
6441\permanent\protected\def\setflag  #1{\dodoglobal\letcsname\??flag#1\endcsname\zerocount}
6442\permanent\protected\def\resetflag#1{\dodoglobal\letcsname\??flag#1\endcsname\plusone}
6443
6444\permanent\def\flag#1{\csname\??flag#1\endcsname}
6445
6446\permanent\def\doifelseflagged#1%
6447  {\expandafter\ifrelax\csname\??flag#1\endcsname
6448     \expandafter\secondoftwoarguments
6449   \orelse\ifcase\csname\??flag#1\endcsname
6450     \expandafter\firstoftwoarguments
6451   \else
6452     \expandafter\secondoftwoarguments
6453   \fi}
6454
6455\aliased\let\doifflaggedelse\doifelseflagged
6456
6457\permanent\def\doifnotflagged#1%
6458  {\expandafter\ifrelax\csname\??flag#1\endcsname
6459     \expandafter\firstofoneargument
6460   \orelse\ifcase\csname\??flag#1\endcsname
6461     \expandafter\gobbleoneargument
6462   \else
6463     \expandafter\firstofoneargument
6464   \fi}
6465
6466\permanent\protected\def\inheritparameter[#1]#2[#3]#4[#5]% tag tokey fromkey
6467  {\defcsname#1#3\expandafter\endcsname\expandafter{\csname#1#5\endcsname}}
6468
6469\def\syst_helpers_if_non_zero_positive_else#1#2\end % #3#4%
6470  {\ifrelax#1%
6471     \ifcase\privatescratchcounter
6472       \endgroup
6473       \doubleexpandafter\secondoftwoarguments
6474     \else
6475       \endgroup
6476       \doubleexpandafter\firstoftwoarguments
6477     \fi
6478   \else
6479      \endgroup
6480      \expandafter\secondoftwoarguments
6481   \fi}
6482
6483% used ?
6484
6485\permanent\def\doifelsenonzeropositive#1%
6486  {\begingroup\afterassignment\syst_helpers_if_non_zero_positive_else\privatescratchcounter=0#1\relax\empty\end}
6487
6488\aliased\let\doifnonzeropositiveelse\doifelsenonzeropositive
6489
6490%D Some old|-|time favourites:
6491
6492% \permanent\protected\def\dosetrawvalue #1#2#3{\defcsname #1#2\endcsname{#3}}
6493% \permanent\protected\def\dosetrawevalue#1#2#3{\edefcsname#1#2\endcsname{#3}}
6494% \permanent\protected\def\dosetrawgvalue#1#2#3{\gdefcsname#1#2\endcsname{#3}}
6495% \permanent\protected\def\dosetrawxvalue#1#2#3{\xdefcsname#1#2\endcsname{#3}}
6496%
6497% \permanent\protected\def\getrawparameters      {\dogetparameters\dosetrawvalue }
6498% \permanent\protected\def\getraweparameters     {\dogetparameters\dosetrawevalue}
6499% \permanent\protected\def\getrawgparameters     {\dogetparameters\dosetrawgvalue}
6500% \permanent\protected\def\getrawxparameters     {\dogetparameters\dosetrawxvalue}
6501%
6502% \permanent\protected\def\globalgetrawparameters{\dogetparameters\dosetrawgvalue} % obsolete
6503
6504\aliased\let\getrawparameters \getparameters  % obsolete
6505\aliased\let\getraweparameters\geteparameters % obsolete
6506\aliased\let\getrawgparameters\getgparameters % obsolete
6507\aliased\let\getrawxparameters\getxparameters % obsolete
6508
6509%D Sort of obsolete:
6510
6511\newinteger\c_syst_helpers_mod
6512
6513\permanent\protected\def\dosetmodulo#1#2#3%
6514  {\c_syst_helpers_mod#1\divideby\c_syst_helpers_mod#2\multiplyby\c_syst_helpers_mod#2%
6515   #3#1\advanceby#3-\c_syst_helpers_mod}
6516
6517\permanent\protected\def\dosetdivision#1#2#3%
6518  {#3#1\divideby#3 #2\relax}
6519
6520\permanent\protected\def\DoMod#1by#2to#3{\dosetmodulo  {#1}{#2}{#3}}
6521\permanent\protected\def\DoDiv#1by#2to#3{\dosetdivision{#1}{#2}{#3}}
6522
6523%D This is obsolete, just use \type {\unprotect} and \type {\protect} instead.
6524
6525% \def\syst_helpers_unprotected#1\par
6526%   {#1\protect}
6527%
6528% \permanent\protected\def\unprotected
6529%   {\unprotect
6530%    \syst_helpers_unprotected}
6531
6532\aliased\let\resettimer    \clf_resettimer  % todo: at lua end
6533\aliased\let\elapsedtime   \clf_elapsedtime % todo: at lua end
6534\aliased\let\elapsedseconds\elapsedtime
6535
6536\aliased\let\elapsedsteps\!!zerocount
6537
6538\permanent\protected\def\elapsedsteptime % unexpanded ! a bit useless but who knows ...
6539  {\clf_elapsedsteptime\elapsedsteps\relax}
6540
6541\newinteger\c_syst_helpers_test_feature_n
6542\newinteger\c_syst_helpers_test_feature_m
6543
6544\let\syst_helpers_test_feature_yes\relax
6545\let\syst_helpers_test_feature_nop\relax
6546
6547\permanent\def\currentfeaturetest{\the\c_syst_helpers_test_feature_n}
6548
6549\permanent\protected\def\testfeature#1#2%
6550  {\c_syst_helpers_test_feature_m\ifparameter#1\or#1\else\maxiterator\fi\relax
6551   \enforced\xdef\elapsedsteps{\the\c_syst_helpers_test_feature_m}%
6552   \def\syst_helpers_test_feature_yes
6553     {\advanceby\c_syst_helpers_test_feature_n\plusone
6554      \ifnum\c_syst_helpers_test_feature_n>\c_syst_helpers_test_feature_m\else
6555        #2\expandafter\syst_helpers_test_feature_yes
6556      \fi}%
6557   \def\syst_helpers_test_feature_nop
6558     {\advanceby\c_syst_helpers_test_feature_n\plusone
6559      \ifnum\c_syst_helpers_test_feature_n>\c_syst_helpers_test_feature_m\else
6560        \expandafter\syst_helpers_test_feature_nop
6561      \fi}%
6562   \retestfeature}
6563
6564\permanent\protected\def\retestfeature % timer support is new per 10/5/2005
6565  {\bgroup
6566   \ifcase\interactionmode\enforced\let\wait\relax\fi
6567   \clf_resettimer
6568   \c_syst_helpers_test_feature_n\zerocount
6569   \syst_helpers_test_feature_nop
6570   \clf_benchmarktimer
6571   \writestatus\m!system
6572     {starting feature test: %
6573      \the\c_syst_helpers_test_feature_m\space steps}%
6574   \c_syst_helpers_test_feature_n\zerocount
6575   \syst_helpers_test_feature_yes
6576   \writestatus\m!system
6577     {feature test done: %
6578      \the\c_syst_helpers_test_feature_m\space steps, %
6579      \clf_elapsedtime\space seconds, %
6580      \clf_elapsedsteptime\elapsedsteps\space\space per step}%
6581   \egroup}
6582
6583\permanent\protected\def\showtimer#1%
6584  {\writestatus{runtime}{\elapsedseconds\space s / #1}}
6585
6586\permanent\protected\def\testfeatureonce#1#2%
6587  {\begingroup
6588   \enforced\let\wait\relax
6589   \testfeature{#1}{#2}%
6590   \endgroup}
6591
6592%D \macros
6593%D   {freezedimenmacro}
6594%D
6595%D This macro is use as:
6596%D
6597%D \starttyping
6598%D \freezedimenmacro\leftmargindistance
6599%D \stoptyping
6600
6601\permanent\protected\def\freezedimenmacro#1%
6602  {\edef#1{\the\dimexpr#1}}
6603
6604%D The next macro negates a macro (dimension or number, or actually, whatever. It's
6605%D a typical example of \type {\if} usage:
6606%D
6607%D \starttyping
6608%D \if-\whatever \else-\whatever\fi => else => -whatever
6609%D \if--\whatever\else-\whatever\fi => then =>  whatever
6610%D \stoptyping
6611%D
6612%D Do we still need this? If not it will go away:
6613
6614\permanent\def\negated#1{\if-#1\else-#1\fi} % does only work in macros or text
6615
6616% \def\gobbleassigndimen#1\\{}
6617%
6618% \permanent\def\assigndimen#1#2%
6619%   {\afterassignment\gobbleassigndimen#1=#2\zeropoint\\}
6620
6621%D Maybe ... toksapp should deal with this ...
6622
6623\permanent\protected\def\appendvalue#1#2%
6624  {\edefcsname#1\endcsname{%
6625     \normalunexpanded\expandafter\expandafter\expandafter{\begincsname#1\endcsname}%
6626     \normalunexpanded{#2}}}
6627
6628\permanent\protected\def\prependvalue#1#2%
6629  {\edefcsname#1\endcsname{%
6630     \normalunexpanded{#2}%
6631     \normalunexpanded\expandafter\expandafter\expandafter{\begincsname#1\endcsname}}}
6632
6633\permanent\protected\def\appendgvalue #1#2{\global\appendvalue}
6634\permanent\protected\def\prependgvalue#1#2{\global\prependvalue}
6635
6636%D \macros
6637%D   {dowithrange}
6638%D
6639%D This one is for Mojca Miklavec, who made me aware of the fact that \type
6640%D {page-imp.tex} was not the best place to hide it.
6641%D
6642%D \startbuffer
6643%D \def\DoSomething#1{ [item #1] }
6644%D
6645%D \processranges[1,4:5]\DoSomething \par
6646%D \dowithrange  {1,4:5}\DoSomething \par
6647%D \stopbuffer
6648%D
6649%D \typebuffer \blank \getbuffer \blank
6650
6651%D We have a better splitter at the \LUA\ end.
6652
6653\integerdef\c_helpers_range_min\zerocount
6654\integerdef\c_helpers_range_max\zerocount
6655
6656\lettonothing\m_helpers_range_action
6657
6658\def\syst_helpers_with_range#1%
6659  {\splitstring#1\at:\to\m_syst_helpers_range_from\and\m_syst_helpers_range_to
6660   \ifx\m_syst_helpers_range_from\wildcardsymbol
6661     \edef\m_syst_helpers_range_from{\number\c_helpers_range_min}%
6662   \fi
6663   \ifx\m_syst_helpers_range_to\wildcardsymbol
6664     \edef\m_syst_helpers_range_to{\number\c_helpers_range_max}%
6665   \fi
6666   \ifempty\m_syst_helpers_range_to
6667     \let\m_syst_helpers_range_to\m_syst_helpers_range_from
6668   \fi
6669   \dostepwiserecurse\m_syst_helpers_range_from\m_syst_helpers_range_to\plusone{\m_helpers_range_action{##1}}}%
6670
6671\permanent\protected\def\processranges[#1]#2% #1= n:m,p,q:r
6672  {\def\m_helpers_range_action{#2}%
6673   \integerdef\c_helpers_range_min\zerocount
6674   \integerdef\c_helpers_range_max\zerocount
6675   \processcommacommand[#1]\syst_helpers_with_range}
6676
6677\permanent\protected\def\dowithrange#1#2% set action
6678  {\def\m_helpers_range_action{#2}%
6679   \integerdef\c_helpers_range_min\zerocount
6680   \integerdef\c_helpers_range_max\zerocount
6681   \processcommacommand[#1]\syst_helpers_with_range}
6682
6683\permanent\protected\def\dowithminmaxrange#1#2#3#4% min max set action
6684  {\def\m_helpers_range_action{#4}%
6685   \integerdef\c_helpers_range_min#1\relax
6686   \integerdef\c_helpers_range_max#2\relax
6687   \processcommacommand[#3]\syst_helpers_with_range}
6688
6689% \def\DoSomething#1{ [item #1] }
6690% \dowithrange[1,4:5]\DoSomething
6691
6692%D \macros
6693%D   {ignoreimplicitspaces}
6694%D
6695%D \startbuffer
6696%D \def\whatever[#1]{\expanded{\definedfont[#1 at 12pt]}\ignorespaces}
6697%D {a\whatever[Serif]b a\whatever[Serif] b a\whatever[Serif]\space b}
6698%D \def\whatever[#1]{\expanded{\definedfont[#1 at 12pt]}\ignoreimplicitspaces}
6699%D {a\whatever[Serif]b a\whatever[Serif] b a\whatever[Serif]\space b}
6700%D \stopbuffer
6701%D
6702%D \typebuffer \getbuffer
6703
6704\permanent\protected\def\ignoreimplicitspaces
6705  {\doifelsenextchar\relax\relax\relax}
6706
6707% %D \macros
6708% %D   {processwords}
6709% %D
6710% %D Not that sophisticated but sometimes users (like in metafun).
6711%
6712% %D This will be overloaded.
6713%
6714% \def\syst_helpers_process_word#1 #2\s!e_o_t_token
6715%   {\doifsomething{#1}{\processword{#1} \syst_helpers_process_word#2 \s!e_o_t_token}}
6716%
6717% \def\processwords#1%
6718%   {\syst_helpers_process_word#1 \s!e_o_t_token}% no \unskip
6719%
6720% \let\processword\relax
6721
6722%D \macros
6723%D   {startnointerference}
6724%D
6725%D \starttyping
6726%D \startnointerference
6727%D all kind of code
6728%D \stopnointerference
6729%D \stoptyping
6730
6731\newbox\b_syst_helpers_no_interference
6732
6733\permanent\protected\def\startnointerference % not even grouped !
6734  {\setbox\b_syst_helpers_no_interference\vbox
6735   \bgroup}
6736
6737\permanent\protected\def\stopnointerference
6738  {\egroup
6739   \setbox\b_syst_helpers_no_interference\emptybox}
6740
6741%D A variant for \type {\executeifdefined}:
6742
6743\permanent\def\expandcheckedcsname#1#2% #2 is often a \xxxparameter  so let's expand it once
6744  {\normalexpanded{\noexpand\syst_helpers_expand_checked_csname{#1}{#2}}}
6745
6746\def\syst_helpers_expand_checked_csname#1#2#3%
6747  {\csname#1\ifcsname#1#2\endcsname#2\else#3\fi\endcsname}
6748
6749%D Signal. Some fonts have a char0 rendering so we need to make sure that it is not
6750%D set in the font! (This will be overloaded)
6751
6752\protected\def\signalcharacter{\char\zerocount} % \zwj
6753
6754% %D A few secial variants of commands defined here. Some more will be moved here
6755% %D e.g.from the table modules. This code is no longer needed.
6756%
6757% \permanent\def\dodirectdoubleempty#1#2% used in math (lookahead issues)
6758%   {\ifx#2[%
6759%      \expandafter\syst_helpers_direct_double_empty_one_yes
6760%    \else
6761%      \expandafter\syst_helpers_direct_double_empty_one_nop
6762%    \fi#1#2}
6763%
6764% \def\syst_helpers_direct_double_empty_one_yes#1[#2]#3%
6765%   {\ifx#3[\else\expandafter\syst_helpers_direct_double_empty_two_nop\fi#1[#2]#3}
6766%
6767% \def\syst_helpers_direct_double_empty_one_nop#1{#1[][]}
6768% \def\syst_helpers_direct_double_empty_two_nop#1[#2]{#1[#2][]}
6769
6770% %D Used in math definitions (in an \type {\edef}). This will be replaced.
6771% %D
6772% %D \startbuffer
6773% %D [\docheckedpair{}]
6774% %D [\docheckedpair{a}]
6775% %D [\docheckedpair{a,b}]
6776% %D [\docheckedpair{a,b,c}]
6777% %D \stopbuffer
6778% %D
6779% %D \typebuffer \startlines \getbuffer \stoplines
6780%
6781% \permanent\def\docheckedpair#1%
6782%   {\syst_helpers_checked_pair#1,,\s!e_o_t_token}
6783%
6784% \def\syst_helpers_checked_pair#1,#2,#-\s!e_o_t_token
6785%   {#1,#2}
6786
6787%D Here are some nasty helpers. They can be used to fill often expanded token
6788%D lists efficiently (see tabulate for an example).
6789
6790\permanent\def\constantnumber#1%
6791  {\ifcase#1\zerocount
6792   \or      \plusone
6793   \or      \plustwo
6794   \or      \plusthree
6795   \or      \plusfour
6796   \or      \plusfive
6797   \or      \plussix
6798   \or      \plusseven
6799   \or      \pluseight
6800   \or      \plusnine
6801   \or      \plusten
6802   \else    \number#1\relax\fi}
6803
6804\permanent\def\constantnumberargument#1%
6805  {\ifcase#1\zerocount
6806   \or      \plusone
6807   \or      \plustwo
6808   \or      \plusthree
6809   \or      \plusfour
6810   \or      \plusfive
6811   \or      \plussix
6812   \or      \plusseven
6813   \or      \pluseight
6814   \or      \plusnine
6815   \or      \plusten
6816   \else    {\number#1}\fi}
6817
6818% \permanent\def\constantdimen#1% takes register
6819%   {\ifdim#1=\zeropoint
6820%      \zeropoint
6821%    \else
6822%      \the#1\relax
6823%    \fi}
6824
6825\permanent\def\constantdimen#1% takes register
6826  {\ifzeropt#1\norelax
6827     \zeropoint
6828   \else
6829     \todimension#1\norelax
6830   \fi}
6831
6832% \permanent\def\constantdimenargument#1% takes register
6833%   {\ifdim#1=\zeropoint
6834%      \zeropoint
6835%    \else
6836%      {\the#1}%
6837%    \fi}
6838
6839\permanent\def\constantdimenargument#1%
6840  {\ifzeropt#1%\norelax
6841     \zeropoint
6842   \else
6843     {\todimension#1}%
6844   \fi}
6845
6846\permanent\def\constantemptyargument#1%
6847  {\ifempty#1%
6848     \noexpand\empty
6849   \else
6850     {#1}%
6851   \fi}
6852
6853%D \macros
6854%D   {getsubstring}
6855%D \startbuffer
6856%D
6857%D \getsubstring{4}{}{Who Wants This}
6858%D \getsubstring{4}{9}{Who Wants This}
6859%D \getsubstring{9}{-2}{Who Wants This}
6860%D \getsubstring{1}{5}{Who Wants This}
6861%D \stopbuffer
6862%D
6863%D \typebuffer
6864%D
6865%D \startlines
6866%D \getbuffer
6867%D \stoplines
6868
6869% expandable:
6870
6871\permanent\def\getsubstring#1#2#3{\clf_getsubstring{#3}{#1}{#2}}
6872
6873%D Other dimensions than pt (used in mb-mp)
6874
6875\permanent\def\converteddimen#1#2{\clf_converteddimen\dimexpr#1\relax{#2}}
6876
6877%D Maybe (looks ugly):
6878%D
6879%D \starttyping
6880%D \doifcase {foo}
6881%D     {bar}     {BAR}
6882%D     {foo}     {FOO}
6883%D     {default} {DEFAULT}
6884%D
6885%D \doifcase {foo}
6886%D     {bar}     {BAR}
6887%D     {foo}     {\doifcase {bar}
6888%D                     {bar}     {BAR}
6889%D                     {foo}     {FOO}
6890%D                     {default} {DEFAULT}
6891%D               }
6892%D     {default} {DEFAULT}
6893%D \stoptyping
6894
6895% \doifcase {\btxfoundname{author}}
6896%     {author}  {\btxflush{author}}
6897%     {editor}  {\texdefinition{btx:apa:editor-or-editors}}
6898%     {title}   {\texdefinition{btx:apa:title-subtitle-type}}
6899%     {default} {\btxflush{author}}
6900
6901% \protected\def\doifcase#1%
6902%   {\edef\m_case_asked{#1}%
6903%    \syst_aux_case}
6904%
6905% \def\syst_aux_case#1%
6906%   {\edef\m_case_temp{#1}%
6907%    \ifx\m_case_temp\m_case_asked
6908%       \expandafter\syst_aux_case_yes
6909%    \orelse\ifx\m_case_temp\s!default
6910%       \expandafter\firstofoneargument
6911%    \else
6912%       \expandafter\syst_aux_case_nop
6913%    \fi}
6914%
6915% \def\syst_aux_skip#1#2%
6916%   {\edef\m_case_temp{#1}%
6917%    \ifx\m_case_temp\s!default
6918%      \expandafter\syst_aux_done
6919%    \else
6920%      \expandafter\syst_aux_skip
6921%    \fi}
6922%
6923% \def\syst_aux_case_yes#1%
6924%   {\def\syst_aux_done{#1}%
6925%    \syst_aux_skip}
6926%
6927% \def\syst_aux_case_nop#1%
6928%   {\syst_aux_case}
6929
6930%D \macros
6931%D   {ntimes}
6932%D
6933%D some repetition:
6934%D
6935%D \startbuffer
6936%D \ntimes{*}{20}
6937%D \stopbuffer
6938%D
6939%D \typebuffer \blank gives: \getbuffer \blank
6940%D
6941%D This is not real fast but quite okay:
6942
6943%def\ntimes#1#2{\ifnum#2>\zerocount#1\ntimes{#1}{\numexpr#2-\plusone\relax}\fi} % 1.72
6944\permanent\def\ntimes#1#2{\clf_ntimes{#1}\numexpr#2\relax}                                % 0.33
6945
6946%D Experiment (sometimes looks nicer in code):
6947
6948\permanent\protected\def\sameargumentscondition#1#2%
6949  {\edef\m_syst_string_one{#1}%
6950   \edef\m_syst_string_two{#2}%
6951   \ifx\m_syst_string_one\m_syst_string_two}
6952
6953\permanent\protected\def\emptyargumentcondition#1%
6954  {\edef\m_syst_string_one{#1}%
6955   \ifempty\m_syst_string_one}
6956
6957%D New (also serves as an example):
6958%D
6959%D \starttyping
6960%D \commandflags\defineframed
6961%D \stoptyping
6962
6963\permanent\def\commandflags#1%
6964  {\beginlocalcontrol\begingroup\scratchtoks\emptytoks\donefalse
6965     \ifflags#1=   \frozenflagcode\etoksapp\scratchtoks{\ifdone \space\fi    frozen}\donetrue\fi
6966     \ifflags#1=\permanentflagcode\etoksapp\scratchtoks{\ifdone \space\fi permanent}\donetrue\fi
6967     \ifflags#1=\immutableflagcode\etoksapp\scratchtoks{\ifdone \space\fi immutable}\donetrue\fi
6968     \ifflags#1=\primitiveflagcode\etoksapp\scratchtoks{\ifdone \space\fi primitive}\donetrue\fi
6969     \ifflags#1=  \mutableflagcode\etoksapp\scratchtoks{\ifdone \space\fi   mutable}\donetrue\fi
6970     \ifflags#1=\noalignedflagcode\etoksapp\scratchtoks{\ifdone \space\fi noaligned}\donetrue\fi
6971     \ifflags#1= \instanceflagcode\etoksapp\scratchtoks{\ifdone \space\fi  instance}\donetrue\fi
6972    %\ifflags#1= \reservedflagcode\etoksapp\scratchtoks{\ifdone \space\fi  reserved}\donetrue\fi
6973     \ifflags#1= \tolerantflagcode\etoksapp\scratchtoks{\ifdone \space\fi  tolerant}\donetrue\fi
6974     \ifflags#1=\protectedflagcode\etoksapp\scratchtoks{\ifdone \space\fi protected}\donetrue\fi
6975   \expandafter\endgroup\expandafter\endlocalcontrol\the\scratchtoks}
6976
6977%D \macros
6978%D   {resetmacros}
6979%D
6980%D The next macro can be used to reset a macro:
6981%D
6982%D \starttyping
6983%D \resetmacros[startfoo,\stopfoo]
6984%D \stoptyping
6985
6986\permanent\protected\def\syst_reset_macro#1%
6987  {\overloaded\letcsname\csstring#1\endcsname\undefined} % so only frozen (instances(
6988
6989\permanent\protected\def\resetmacros[#1]%
6990  {\processcommalist[#1]\syst_reset_macro}
6991
6992%D These demos are for hvdm who needs ways to manipulate arguments but
6993%D in a fully expandable way (some explanation is given in the low level
6994%D expansion manual).
6995%D
6996%D \startbuffer
6997%D \edef\xxx{[\wipetokens {123}{abc123abc123abc123abc123abc}]}1 : \meaningless\xxx\par
6998%D \edef\xxx{[\wipetokens {123}{abc}]}                        2 : \meaningless\xxx\par
6999%D \edef\xxx{[\wipetokens {123}{123}]}                        3 : \meaningless\xxx\par
7000%D \edef\xxx{[\wipetokens {123}{123123}]}                     4 : \meaningless\xxx\par
7001%D \edef\xxx{[\wipetokens {123}{}]}                           5 : \meaningless\xxx\par
7002%D \edef\xxx{[\wipetokens {}{123}]}                           6 : \meaningless\xxx\par
7003%D \edef\xxx{[\wipetokens {\relax}{1\relax2\relax3}]}         7 : \meaningless\xxx\par
7004%D \edef\xxx{[\wipetokens {1}{1{2}3}]}                        8 : \meaningless\xxx\par
7005%D \edef\xxx{[\wipedtokens{{1}}{{1}23}]}                      9 : \meaningless\xxx\par
7006%D \edef\xxx{[\wipedtokens{{1}}{123}]}                        0 : \meaningless\xxx\par
7007%D \stopbuffer
7008%D
7009%D \typebuffer \startpacked \getbuffer \stoppacked
7010
7011\lettonothing\syst_helpers_wipe_tokens_yes
7012
7013\def\syst_helpers_wipe_tokens_nop#-^^04{}%
7014
7015\permanent\def\wipetokens#1#2%
7016  {\beginlocalcontrol
7017   \tolerant\def\syst_helpers_wipe_tokens_yes##1#1##2^^04%
7018     {##1%
7019      \ifparameter##2\or
7020        \expandafter\syst_helpers_wipe_tokens_yes
7021      \else
7022        \expandafter\syst_helpers_wipe_tokens_nop
7023      \fi
7024      ##2^^04}%
7025   \endlocalcontrol
7026   \syst_helpers_wipe_tokens_yes#2#1^^04}
7027
7028\permanent\def\wipedtokens#1#2%
7029  {\tokenized{\normalexpanded{\noexpand\wipetokens{\detokenize{#1}}{\detokenize{#2}}}}}
7030
7031%D \startbuffer
7032%D                \def\abc{abc}
7033%D \semiprotected \def\xyz{xyz}
7034%D               \edef\pqr{\expandtoken\notcatcodes`p%
7035%D                         \expandtoken\notcatcodes`q%
7036%D                         \expandtoken\notcatcodes`r}
7037%D
7038%D 1: \ifcondition\similartokens{abc} {def}YES\else NOP\fi (NOP) \quad
7039%D 2: \ifcondition\similartokens{abc}{\abc}YES\else NOP\fi (YES)
7040%D
7041%D 3: \ifcondition\similartokens{xyz} {pqr}YES\else NOP\fi (NOP) \quad
7042%D 4: \ifcondition\similartokens{xyz}{\xyz}YES\else NOP\fi (YES)
7043%D
7044%D 5: \ifcondition\similartokens{pqr} {pqr}YES\else NOP\fi (YES) \quad
7045%D 6: \ifcondition\similartokens{pqr}{\pqr}YES\else NOP\fi (YES)
7046%D \stopbuffer
7047%D
7048%D \typebuffer \startpacked \getbuffer \stoppacked
7049
7050\permanent\protected\def\similartokens#1#2%
7051 %{\normalexpanded{\noexpand\iftok{\noexpand\detokenize{#1}}{\noexpand\detokenize{#2}}}}
7052  {\semiexpanded{\noexpand\iftok{\noexpand\detokenize{#1}}{\noexpand\detokenize{#2}}}}
7053
7054\permanent\protected\def\doifelsesimilartokens#1#2%
7055  {\ifcondition\similartokens{#1}{#2}%
7056     \expandafter\firstoftwoarguments
7057   \else
7058     \expandafter\secondoftwoarguments
7059   \fi}
7060
7061%D Here (can become primitive):
7062
7063\untraced\permanent\protected\def\glyphscaled#1% name will change
7064  {\dimexpr#1*\plusthousand/\glyphscale\relax}
7065
7066\protect \endinput
7067