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