diff options
author | Taco Hoekwater <taco@elvenkind.com> | 2009-08-23 11:11:32 +0000 |
---|---|---|
committer | Taco Hoekwater <taco@elvenkind.com> | 2009-08-23 11:11:32 +0000 |
commit | 8fc3039c82d48605b5ca8b2eda3f4fdd755681e1 (patch) | |
tree | 3cd9bbdd599bc4d1ac0409e167fee2136e4c0ec9 /Master/texmf-dist/tex/context/base/syst-ext.mkii | |
parent | 850fc99b7cd3ae7a20065531fe866ff7bae642ec (diff) |
this is context 2009.08.19 17:10
git-svn-id: svn://tug.org/texlive/trunk@14827 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/context/base/syst-ext.mkii')
-rw-r--r-- | Master/texmf-dist/tex/context/base/syst-ext.mkii | 5004 |
1 files changed, 5004 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/syst-ext.mkii b/Master/texmf-dist/tex/context/base/syst-ext.mkii new file mode 100644 index 00000000000..898ee8000a5 --- /dev/null +++ b/Master/texmf-dist/tex/context/base/syst-ext.mkii @@ -0,0 +1,5004 @@ +%D \module +%D [ file=syst-ext, +%D version=1995.10.10, +%D title=\CONTEXT\ System Macros, +%D subtitle=Extras, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt System Macros / Extras} + +%D In this second system module, we continue the definition of +%D some handy commands. + +\unprotect + +%D \macros +%D {rawgetparameters} +%D +%D A raw and dirty alternative for \type {\getparameters}; no +%D checking is done! + +% \def\rawgetparameters[#1][#2]% scheelt 5\% +% {\def\rawparameterprefix{#1}% +% \expandafter\rawsetparameter#2,]=,} + +\def\rawsetparameter#1=#2,% + {\if]#1\else + \expandafter\def\csname\rawparameterprefix#1\endcsname{#2}% + \expandafter\rawsetparameter + \fi} + +% the next one handles empty #1 okay: + +\def\rawgetparameters[#1][#2% some 5-10% faster + {\ifx#2]% test is needed, else bomb on [#1][] + \expandafter\gobbleoneargument + \else + \def\rawparameterprefix{#1}% + \expandafter\dorawgetparameters + \fi#2} + +\def\dorawgetparameters#1]% + {\expandafter\rawsetparameter#1,]=,} + +%D \macros +%D {doglobal, +%D redoglobal,dodoglobal,resetglobal} +%D +%D The two macros \type {\redoglobal} and \type{\dodoglobal} are +%D used in this and some other modules to enforce a user +%D specified \type {\doglobal} action. The last and often only +%D global assignment in a macro is done with +%D \type {\dodoglobal}, but all preceding ones with +%D \type {\redoglobal}. When using only alternatives, one can +%D reset this mechanism with \type {\resetglobal}. + +\def\doglobal + {\let\redoglobal\global + \def\dodoglobal{\resetglobal\global}} + +\def\resetglobal + {\let\redoglobal\relax + \let\dodoglobal\relax} + +\resetglobal + +%D New: + +\def\doglobal + {\ifx\redoglobal\relax + \let\redoglobal\global + \let\dodoglobal\@@dodoglobal + %\else + % \writestatus{system}{global not reset, warn me!}% + \fi} + +\def\@@dodoglobal + {\resetglobal\global} + +\def\saveglobal + {\let\@@dodoglobal\dodoglobal + \let\@@redoglobal\redoglobal} + +\def\restoreglobal + {\let\redoglobal\@@redoglobal + \let\dodoglobal\@@dodoglobal} + +%D A very useful application of this macro is \type {\newif}, +%D \TEX's fake boolean type. Not being a primitive, +%D \type {\global} hopelessly fails here. But a slight +%D adaption of Knuth's original macro permits: +%D +%D \starttyping +%D \doglobal\newif\iftest +%D \stoptyping +%D +%D Of course one can still say: +%D +%D \starttyping +%D \global\testtrue +%D \global\testfalse +%D \stoptyping +%D +%D Apart from the prefixes, a few more \type{\expandafters} +%D are needed: + +\def\newif#1% + {\scratchcounter\escapechar + \escapechar\minusone + \expandafter\expandafter\expandafter + \redoglobal\expandafter\expandafter\expandafter + \edef\@if#1{true}{\let\noexpand#1\noexpand\iftrue}% + \expandafter\expandafter\expandafter + \redoglobal\expandafter\expandafter\expandafter + \edef\@if#1{false}{\let\noexpand#1\noexpand\iffalse}% + \dodoglobal\@if#1{false}% + \escapechar\scratchcounter} + +%D Also new: + +\def\define#1% + {\ifx#1\undefined + \expandafter\def + \else + \message{[\noexpand#1is already defined]}% + \expandafter\def\expandafter\gobbleddefinition + \fi#1} + +\def\redefine#1% + {\ifx#1\undefined\else + \message{[\noexpand#1is redefined]}% + \fi + \def#1} + +% \define\hans{hans} +% \redefine\hans{hans} +% \define\hans#1[]#2#3{hans} + +%D The next variant fits nicely in the setups syntax: +%D +%D \starttyping +%D \starttexdefinition bagger [#1] #2 +%D oeps +%D #1 +%D oeps +%D \stoptexdefinition +%D +%D \bagger [a] {b} +%D \stoptyping + +\bgroup \obeylines + +\gdef\starttexdefinition% + {\bgroup% + \obeylines% + \dostarttexdefinition} + +\gdef\dostarttexdefinition #1 + {\catcode13=\@@ignore% + \doifinstringelse\letterhash{\detokenize{#1}}\dodostarttexdefinition\nonostarttexdefinition#1 + } + +\long\gdef\dodostarttexdefinition#1 #2 + {\dododostarttexdefinition{#1}{#2}} + +\long\gdef\dododostarttexdefinition#1#2#3\stoptexdefinition% + {\egroup% + \long\setvalue{#1}#2{#3}} + +\long\gdef\nonostarttexdefinition#1 + {\nononostarttexdefinition{#1}{}} + +\long\gdef\nononostarttexdefinition#1#2#3\stoptexdefinition% + {\egroup% + \long\setvalue{#1}{#3}} + +\egroup + +%D \macros +%D {newcounter, +%D increment,decrement} +%D +%D Unfortunately the number of \COUNTERS\ in \TEX\ is limited, +%D but fortunately we can store numbers in a macro. We can +%D increment such pseudo \COUNTERS\ with \type{\increment}. +%D +%D \starttyping +%D \increment(\counter,20) +%D \increment(\counter,-4) +%D \increment(\counter) +%D \increment\counter +%D \stoptyping +%D +%D After this sequence of commands, the value of +%D \type{\counter} is 20, 16, 17 and~18. Of course there is +%D also the complementary command \type{\decrement}. +%D +%D Global assignments are possible too, using \type{\doglobal}: +%D +%D \starttyping +%D \doglobal\increment\counter +%D \stoptyping +%D +%D When \type{\counter} is undefined, it's value is initialized +%D at~0. It is nevertheless better to define a \COUNTER\ +%D explicitly. One reason could be that the \COUNTER\ can be +%D part of a test with \type{\ifnum} and this conditional does +%D not accept undefined macro's. The \COUNTER\ in our example +%D can for instance be defined with: +%D +%D \starttyping +%D \newcounter\counter +%D \stoptyping +%D +%D The command \type{\newcounter} must not be confused with +%D \type{\newcount}! Of course this mechanism is much slower +%D than using \TEX's \COUNTERS\ directly. In practice +%D \COUNTERS\ (and therefore our pseudo counters too) are +%D seldom the bottleneck in the processing of a text. Apart +%D from some other incompatilities we want to mention a pitfal +%D when using \type{\ifnum}. +%D +%D \starttyping +%D \ifnum\normalcounter=\pseudocounter \doif \else \doelse \fi +%D \ifnum\pseudocounter=\normalcounter \doif \else \doelse \fi +%D \stoptyping +%D +%D In the first test, \TEX\ continues it's search for the +%D second number after reading \type{\pseudocounter}, while +%D in the second test, it stops reading after having +%D encountered a real one. Tests like the first one therefore +%D can give unexpected results, for instance execution +%D of \type{\doif} even if both numbers are unequal. + +\def\zerocountervalue{0} + +\def\newcounter#1% + {\dodoglobal\let#1\zerocountervalue} + +% This is the original implementation: +% +% \def\dodododoincrement(#1,#2)% +% {\ifx#1\undefined +% \redoglobal\let#1\zerocountervalue +% \else\ifx#1\relax % \csname...\endcsname +% \redoglobal\let#1\zerocountervalue +% \fi\fi +% \scratchcounter=#2\relax +% \scratchcounter=\incrementsign\scratchcounter +% \advance\scratchcounter #1\relax +% \dodoglobal\edef#1{\the\scratchcounter}} +% +% \def\dododoincrement#1% +% {\dodododoincrement(#1,1)} +% +% \def\dodoincrement(#1% +% {\doifnextcharelse,% +% {\dodododoincrement(#1}{\dodododoincrement(#1,1}} +% +% \def\doincrement#1% +% {\def\incrementsign{#1}% +% \doifnextcharelse(\dodoincrement\dododoincrement} +% +% \def\increment{\doincrement+} +% \def\decrement{\doincrement-} +% +% And this is the one optimized for speed: + +% maxcounter = 2\maxdimen=1 + +\def\!!zerocount {0} % alongside \zerocount +\def\!!minusone {-1} % alongside \minusone +\def\!!plusone {1} % alongside \plusone + +\beginTEX + +\def\dodoindecrement#1(#2,#3)% + {\ifx#2\undefined + \redoglobal\let#2\zerocountervalue + \else\ifx#2\relax % \csname...\endcsname + \redoglobal\let#2\zerocountervalue + \fi\fi + \scratchcounter#3\relax + \scratchcounter#1\scratchcounter + \advance\scratchcounter#2\relax + \dodoglobal\edef#2{\the\scratchcounter}} + +\def\dodoincrement(#1% + {\doifnextcharelse,{\dodoindecrement+(#1}{\dodoindecrement+(#1,1}} + +\def\dododecrement(#1% + {\doifnextcharelse,{\dodoindecrement-(#1}{\dodoindecrement-(#1,1}} + +\def\doincrement#1% 10% faster alternative + {\ifx#1\undefined + \dodoglobal\let#1\!!plusone + \else\ifx#1\relax % \csname...\endcsname + \dodoglobal\let#1\!!plusone + \else + \fastincrement#1% + \fi\fi} + +\def\dodecrement#1% 10% faster alternative + {\ifx#1\undefined + \dodoglobal\let#1\!!minusone + \else\ifx#1\relax % \csname...\endcsname + \dodoglobal\let#1\!!minusone + \else + \fastdecrement#1% + \fi\fi} + +\def\fastdecrement#1% 50% faster alternative + {\scratchcounter#1\advance\scratchcounter\minusone + \dodoglobal\edef#1{\the\scratchcounter}} + +\def\fastincrement#1% 50% faster alternative + {\scratchcounter#1\advance\scratchcounter\plusone + \dodoglobal\edef#1{\the\scratchcounter}} + +\endTEX + +\beginETEX \numexpr + +% \def\doindecrement#1#2% +% {\dodoglobal\edef#2% +% {\the\numexpr(\ifx#2\undefined\else\ifx#2\relax\else#2\fi\fi#11)}} +% +% \def\doincrement{\doindecrement+} +% \def\dodecrement{\doindecrement-} +% +% some 3\% faster: + +\def\doindecrement#1#2% + {\dodoglobal\edef#2% + {\the\numexpr\ifx#2\undefined\else\ifx#2\relax\else#2\fi\fi+#1\relax}} + +\def\doincrement{\doindecrement\plusone } +\def\dodecrement{\doindecrement\minusone} + +\def\dodoindecrement#1#2,#3)% + {\dodoglobal\edef#2% + {\the\numexpr\ifx#2\undefined\else\ifx#2\relax\else#2\fi\fi#1#3\relax}} + +\def\dodoincrement(#1% + {\doifnextcharelse,{\dodoindecrement+#1}{\dodoindecrement+#1,\plusone}} + +\def\dododecrement(#1% + {\doifnextcharelse,{\dodoindecrement-#1}{\dodoindecrement-#1,\plusone}} + +\def\fastincrement#1{\dodoglobal\edef#1{\the\numexpr#1+\plusone \relax}} +\def\fastdecrement#1{\dodoglobal\edef#1{\the\numexpr#1+\minusone\relax}} + +\endETEX + +\def\increment{\doifnextcharelse(\dodoincrement\doincrement} +\def\decrement{\doifnextcharelse(\dododecrement\dodecrement} + +\def\incrementvalue#1{\expandafter\increment\csname#1\endcsname} +\def\decrementvalue#1{\expandafter\decrement\csname#1\endcsname} + +%D \macros +%D {newsignal} +%D +%D When writing advanced macros, we cannot do without +%D signaling. A signal is a small (invisible) kern or penalty +%D that signals the next macro that something just happened. +%D This macro can take any action depending on the previous +%D signal. Signals must be unique and the next macro takes care +%D of that. +%D +%D \starttyping +%D \newsignal\somesignal +%D \stoptyping +%D +%D Signals old dimensions and can be used in skips, kerns and +%D tests like \type{\ifdim}. + +\newdimen\maximumsignal % step is about 0.00025pt + +\def\newsignal#1% + {\ifx#1\undefined + \advance\maximumsignal 2sp % to be save in rounding + \edef#1{\the\maximumsignal}% + \fi} + +%D \macros +%D {newskimen} +%D +%D \TEX\ offers 256 \DIMENSIONS\ and \SKIPS. Unfortunately this +%D amount is too small to suit certain packages. Therefore when +%D possible one should use: +%D +%D \starttyping +%D \newskimen\tempskimen +%D \stoptyping +%D +%D This commands allocates a \DIMENSION\ or a \SKIP, depending +%D on the availability. One should be aware of the difference +%D between both. When searching for some glue \TEX\ goes on +%D searching till it's sure that no other glue component if +%D found. This search can be canceled by using \type{\relax} +%D when possible and needed. +%D +%D \starttyping +%D \def\newskimen#1% +%D {\ifx#1\undefined +%D \ifnum\count11>\count12 +%D \newskip#1\relax +%D \else +%D \newdimen#1\relax +%D \fi +%D \fi} +%D \stoptyping +%D +%D In order to make this macro work in plain \TEX\ too, we +%D use the following alternative, which fools \TEX\ about +%D the new commands being \type {\outer} ones. + +% \def\newskimen#1% +% {\ifx#1\undefined +% \csname new\ifnum\count11>\count12 skip\else dimen\fi\endcsname#1% +% \fi} + +\let\newskimen\newdimen % it's all etex or later now + +%D \macros +%D {strippedcsname} +%D +%D The next macro can be very useful when using \type{\csname} +%D like in: +%D +%D \starttyping +%D \csname if\strippedcsname\something\endcsname +%D \stoptyping +%D +%D This expands to \type{\ifsomething}. +%D +%D \starttyping +%D \def\strippedcsname +%D {\expandafter\gobbleoneargument\string} +%D \stoptyping +%D +%D Slower but better: + +\ifx\letterbackslash\undefined + {\catcode`.=0 .catcode`.\ 12 .xdef.letterbackslash{.string\}} % hack +\fi + +\def\strippedcsname#1% this permits \strippedcsname{\xxx} and \strippedcsname{xxx} + {\expandafter\dostrippedcsname\string#1} + +\def\dostrippedcsname#1% + {\if\noexpand#1\letterbackslash\else#1\fi} + +%D \macros +%D {savenormalmeaning} +%D +%D We will use this one in: + +\def\savenormalmeaning#1% + {\ifundefined{normal\strippedcsname#1}% + \letvalue{normal\strippedcsname#1}#1% + \fi} + +%D \macros +%D {newconditional, +%D settrue, setfalse, +%D ifconditional,then} +%D +%D \TEX's lacks boolean variables, although the \PLAIN\ format +%D implements \type{\newif}. The main disadvantage of this +%D scheme is that it takes three hash table entries. A more +%D memory saving alternative is presented here. A conditional +%D is defined by: +%D +%D \starttyping +%D \newconditional\doublesided +%D \setfalse +%D \stoptyping +%D Setting a conditional is done by \type{\settrue} and +%D \type{\setfalse}: +%D +%D \starttyping +%D \settrue\doublesided +%D \setfalse +%D \stoptyping +%D while testing is accomplished by: +%D +%D \starttyping +%D \ifconditional\doublesided ... \else ... \fi +%D \setfalse +%D \stoptyping +%D We cannot use the simple scheme: +%D +%D \starttyping +%D \def\settrue#1{\let#1=\iftrue} +%D \def\settrue#1{\let#1=\iffalse} +%D \stoptyping +%D +%D Such an implementation gives problems with nested +%D conditionals. The next implementation is abaou as fast +%D and just as straightforward: + +\def\settrue #1{\chardef#1\zerocount} +\def\setfalse#1{\chardef#1\plusone} + +\let\newconditional = \setfalse +\let\ifconditional = \ifcase + +\let\then\relax % so that we can say \ifnum1>2\then -) + +%D \macros +%D {ifzeropt} +%D +%D The next macro is both cosmetic and byte saving. It is +%D pretty \type{\if}||safe too. It can be used in cases +%D like: +%D +%D \starttyping +%D \ifzeropt \somedimen ... \else ... \fi +%D \stoptyping + +\let\ifzeropt\ifcase + +%D \macros +%D {dorecurse,recurselevel,recursedepth, +%D dostepwiserecurse, +%D for} +%D +%D \TEX\ does not offer us powerfull for||loop mechanisms. On +%D the other hand its recursion engine is quite unique. We +%D therefore identify the for||looping macros by this method. +%D The most simple alternative is the one that only needs a +%D number. +%D +%D \starttyping +%D \dorecurse {n} {whatever we want} +%D \stoptyping +%D +%D This macro can be nested without problems and therefore be +%D used in situations where \PLAIN\ \TEX's \type{\loop} macro +%D ungracefully fails. The current value of the counter is +%D available in \type{\recurselevel}, before as well as after +%D the \typ{whatever we wat} stuff. +%D +%D \starttyping +%D \dorecurse % inner loop +%D {10} +%D {\recurselevel: % outer value +%D \dorecurse % inner loop +%D {\recurselevel} % outer value +%D {\recurselevel} % inner value +%D \dorecurse % inner loop +%D {\recurselevel} % outer value +%D {\recurselevel} % inner value +%D \endgraf} +%D \stoptyping +%D +%D In this example the first, second and fourth +%D \type{\recurselevel} concern the outer loop, while the third +%D and fifth one concern the inner loop. The depth of the +%D nesting is available for inspection in \type{\recursedepth}. +%D +%D Both \type{\recurselevel} and \type{\recursedepth} are +%D macros. The real \COUNTERS\ are hidden from the user because +%D we don't want any interference. + +\newcount\outerrecurse +\newcount\innerrecurse + +\def\recursedepth{\the\outerrecurse} +\def\recurselevel{0} + +\let\nextrecurse\relax + +%D Acceptable: +%D +%D \starttyping +%D \long\def\dostepwiserecurse#1#2#3% +%D {\let\nextrecurse\gobblefourarguments +%D \ifnum#3>0\relax\ifnum#2<#1\relax\else +%D \def\nextrecurse{\dosetstepwiserecurse>}% +%D \fi\fi +%D \ifnum#3<0\relax\ifnum#1<#2\relax\else +%D \def\nextrecurse{\dosetstepwiserecurse<}% +%D \fi\fi +%D \nextrecurse{#1}{#2}{#3}} +%D \stoptyping +%D +%D Better: +%D +%D \starttyping +%D \long\def\dostepwiserecurse#1#2#3% +%D {\let\nextrecurse\gobblefourarguments +%D \ifnum#3>0\relax \ifnum#2<#1\relax \else +%D \def\nextrecurse{\dosetstepwiserecurse>}% +%D \fi \else \ifnum#3<0\relax \ifnum#1<#2\relax \else +%D \def\nextrecurse{\dosetstepwiserecurse<}% +%D \fi \fi \fi +%D \nextrecurse{#1}{#2}{#3}} +%D +%D \def\@@irecurse{@@irecurse} % stepper +%D \def\@@nrecurse{@@nrecurse} % number of steps +%D \def\@@srecurse{@@srecurse} % step +%D \def\@@drecurse{@@drecurse} % direction, < or > +%D \def\@@arecurse{@@arecurse} % action +%D +%D \long\def\dosetstepwiserecurse#1#2#3#4#5% +%D {\global\advance\outerrecurse 1 +%D \setevalue{\@@drecurse\recursedepth}{#1}% +%D \setevalue{\@@irecurse\recursedepth}{\number#2}% +%D \setevalue{\@@nrecurse\recursedepth}{\number#3}% +%D \setevalue{\@@srecurse\recursedepth}{\number#4}% +%D \long\setvalue{\@@arecurse\recursedepth}{#5}% +%D \dodorecurse} +%D +%D \def\donorecurse +%D {} +%D +%D \def\dododorecurse +%D {\edef\recurselevel{\csname\@@irecurse\recursedepth\endcsname}% +%D \getvalue{\@@arecurse\recursedepth}% +%D \edef\recurselevel{\csname\@@irecurse\recursedepth\endcsname}% +%D \innerrecurse\recurselevel +%D \advance\innerrecurse \csname\@@srecurse\recursedepth\endcsname +%D \setevalue{\@@irecurse\recursedepth}{\the\innerrecurse}% +%D \dodorecurse} +%D +%D \def\dodorecurse +%D {\ifnum\csname\@@irecurse\recursedepth\endcsname +%D \csname\@@drecurse\recursedepth\endcsname +%D \csname\@@nrecurse\recursedepth\endcsname\relax +%D \expandafter\nododorecurse +%D \else +%D \expandafter\dododorecurse +%D \fi} +%D +%D \def\nododorecurse +%D {\global\advance\outerrecurse -1 +%D \edef\recurselevel{\csname\@@irecurse\recursedepth\endcsname}} +%D \stoptyping +%D +%D Cleaner and much faster: + +\def\@@irecurse{@@ir@@} % ecurse} % stepper +\def\@@arecurse{@@ar@@} % ecurse} % action + +% \mathchardef + +\long\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4 + {\global\advance\outerrecurse \plusone + \long\global\@EA\def\csname\@@arecurse\recursedepth\endcsname{#4}% + \global\@EA\let\csname\@@irecurse\recursedepth\endcsname\recurselevel + \ifnum#3>0\relax + \ifnum#2<#1\relax + \let\nextrecurse\exitstepwiserecurse + \else + \let\nextrecurse\dodostepwiserecurse + \fi + \else + \ifnum#3<0\relax + \ifnum#1<#2\relax + \let\nextrecurse\exitstepwiserecurse + \else + \let\nextrecurse\dodostepwisereverse + \fi + \else + \let\nextrecurse\exitstepwiserecurse + \fi + \fi\expanded{\nextrecurse{\number#1}{\number#2}{\number#3}}} + +\beginETEX \numexpr + +\long\def\dodostepwiserecurse#1#2#3% from to step + {\ifnum#1>#2\relax + \@EA\nodostepwiserecurse + \else + \def\recurselevel{#1}% + \@EAEAEA\redostepwiserecurse\@EA + \fi\@EA{\the\numexpr\recurselevel+#3\relax}{#2}{#3}} + +\endETEX + +\beginTEX + +\long\def\dodostepwiserecurse#1#2#3% from to step + {\ifnum#1>#2\relax + \@EA\nodostepwiserecurse + \else + \def\recurselevel{#1}% + \innerrecurse#1\advance\innerrecurse#3\relax + \@EAEAEA\redostepwiserecurse\@EA + \fi\@EA{\the\innerrecurse}{#2}{#3}} + +\endTEX + +\def\expandrecursecontent + {\csname\@@arecurse\recursedepth\endcsname} + +\def\redostepwiserecurse + {\expandrecursecontent\dodostepwiserecurse} + +\beginETEX \numexpr + +\long\def\dodostepwisereverse#1#2#3% from to step + {\ifnum#1<#2\relax + \@EA\nodostepwiserecurse + \else + \def\recurselevel{#1}% + \@EAEAEA\redostepwisereverse\@EA + \fi\@EA{\the\numexpr\recurselevel#3\relax}{#2}{#3}} + +\endETEX + +\beginTEX + +\long\def\dodostepwisereverse#1#2#3% from to step + {\ifnum#1<#2\relax + \@EA\nodostepwiserecurse + \else + \def\recurselevel{#1}% + \innerrecurse#1\relax + \advance\innerrecurse#3\relax + \@EAEAEA\redostepwisereverse\@EA + \fi\@EA{\the\innerrecurse}{#2}{#3}} + +\endTEX + +\def\redostepwisereverse + {\expandrecursecontent\dodostepwisereverse} + +\def\exitstepwiserecurse + {\nodostepwiserecurse\relax} + +\def\nodostepwiserecurse#1#2#3#4% + {\@EA\let\@EA\recurselevel\csname\@@irecurse\recursedepth\endcsname + \global\advance\outerrecurse \minusone} + +\def\nonostepwiserecurse#1#2#3% + {\@EA\let\@EA\recurselevel\csname\@@irecurse\recursedepth\endcsname + \global\advance\outerrecurse \minusone} + +\def\dorecurse#1% + {\dostepwiserecurse1{#1}1} + +%D As we can see here, the simple command \type{\dorecurse} is +%D a special case of the more general: +%D +%D \starttyping +%D \dostepwiserecurse {from} {to} {step} {action} +%D \stoptyping +%D +%D This commands accepts positive and negative steps. Illegal +%D values are handles as good as possible and the macro accepts +%D numbers and \COUNTERS. +%D +%D \starttyping +%D \dostepwiserecurse {1} {10} {2} {...} +%D \dostepwiserecurse {10} {1} {-2} {...} +%D \stoptyping +%D +%D Because the simple case is used often, we implement it +%D more efficiently: + +\long\def\dorecurse#1% + {\ifcase#1\relax + \expandafter\gobbletwoarguments + \or + \expandafter\ydorecurse + \else + \expandafter\xdorecurse + \fi{#1}} + +\long\def\xdorecurse#1#2% + {\global\advance\outerrecurse \plusone + \long\global\@EA\def\csname\@@arecurse\recursedepth\endcsname{#2}% + \global\@EA\let\csname\@@irecurse\recursedepth\endcsname\recurselevel + \@EA\dodorecurse\@EA1\@EA{\number#1}} + +\long\def\ydorecurse#1#2% + {\global\advance\outerrecurse \plusone + \global\@EA\let\csname\@@irecurse\recursedepth\endcsname\recurselevel + \let\recurselevel\!!plusone + #2% + \@EA\let\@EA\recurselevel\csname\@@irecurse\recursedepth\endcsname + \global\advance\outerrecurse \minusone} + +\beginETEX \numexpr + +\long\def\dodorecurse#1#2% from to + {\ifnum#1>#2\relax + \@EA\nodorecurse + \else + \def\recurselevel{#1}% + \@EAEAEA\redorecurse + \fi\@EA{\the\numexpr\recurselevel+\plusone\relax}{#2}} + +\endETEX + +\beginTEX + +\long\def\dodorecurse#1#2% from to + {\ifnum#1>#2\relax + \@EA\nodorecurse + \else + \def\recurselevel{#1}% + \innerrecurse#1\advance\innerrecurse\plusone + \@EAEAEA\redorecurse + \fi\@EA{\the\innerrecurse}{#2}} + +\endTEX + +\def\redorecurse + {\expandrecursecontent\dodorecurse} + +\def\nodorecurse#1#2#3% + {\@EA\let\@EA\recurselevel\csname\@@irecurse\recursedepth\endcsname + \global\advance\outerrecurse \minusone } + +%D \macros +%D {doloop,exitloop} +%D +%D Sometimes loops are not determined by counters, but by +%D (a combinations of) conditions. We therefore implement a +%D straightforward loop, which can only be left when we +%D explictly exit it. Nesting is supported. First we present +%D a more extensive alternative. +%D +%D \starttyping +%D \doloop +%D {Some kind of typesetting punishment \par +%D \ifnum\pageno>100 \exitloop \fi} +%D \stoptyping +%D +%D When needed, one can call for \type{\looplevel} and +%D \type{\loopdepth}. +%D +%D If we write this macros from scratch, we end up with +%D something like the ones described above: +%D +%D \starttyping +%D \def\@@eloop{@@eloop} % exit +%D \def\@@iloop{@@iloop} % stepper +%D \def\@@aloop{@@aloop} % action +%D +%D \newcount\outerloop +%D +%D \def\loopdepth% +%D {\the\outerloop} +%D +%D \def\exitloop% +%D {\setevalue{\@@eloop\loopdepth}{0}} +%D +%D \long\def\doloop#1% +%D {\global\advance\outerloop by 1 +%D \setevalue{\@@iloop\loopdepth}{1}% +%D \setevalue{\@@eloop\loopdepth}{1}% +%D \long\setvalue{\@@aloop\loopdepth}{#1}% +%D \dodoloop} +%D +%D \def\dodonoloop% +%D {\global\advance\outerloop by -1\relax} +%D +%D \def\dododoloop% +%D {\edef\looplevel{\getvalue{\@@iloop\loopdepth}}% +%D \innerrecurse=\looplevel +%D \advance\innerrecurse by 1 +%D \setevalue{\@@iloop\loopdepth}{\the\innerrecurse}% +%D \getvalue{\@@aloop\loopdepth}% +%D \edef\looplevel{\getvalue{\@@iloop\loopdepth}}% +%D \dodoloop} +%D +%D \def\dodoloop% +%D {\ifnum\getvalue{\@@eloop\loopdepth}=0 +%D \expandafter\dodonoloop +%D \else +%D \expandafter\dododoloop +%D \fi} +%D +%D \def\doloop% +%D {\dostepwiserecurse{1}{\maxdimen}{1}} +%D +%D \def\exitloop +%D {\setvalue{\@@irecurse\recursedepth}{\maxdimen}} +%D +%D \def\looplevel{\recurselevel} +%D \def\loopdepth{\recursedepth} +%D \stoptyping +%D +%D We don't have to declare new counters for \type{\looplevel} +%D and \type{\loopdepth} because we can use \type{\recurselevel} +%D and \type{\recursedepth}. +%D +%D We prefer however a more byte saving implementation, that +%D executes of course a bit slower. +%D +%D \starttyping +%D \def\doloop% +%D {\dostepwiserecurse1\maxdimen1} +%D +%D \def\exitloop% +%D {\letvalue{\@@irecurse\recursedepth}\maxdimen} +%D \stoptyping +%D +%D Although, the next version is faster because it used the +%D simple loop. + +\let\endofloop\donothing + +\long\def\doloop#1% + {\global\advance\outerrecurse \plusone + \long\global\@EA\def\csname\@@arecurse\recursedepth\endcsname{#1}% + \global\@EA\let\csname\@@irecurse\recursedepth\endcsname\recurselevel + \let\endofloop\dodoloop + \dodoloop1} % no \plusone else \recurselevel wrong + +\beginETEX \numexpr + +\long\def\dodoloop#1% + {\def\recurselevel{#1}% + \@EA\redoloop\@EA{\the\numexpr\recurselevel+\plusone\relax}} + +\endETEX + +\beginTEX + +\long\def\dodoloop#1% + {\def\recurselevel{#1}% + \innerrecurse#1\advance\innerrecurse\plusone + \@EA\redoloop\@EA{\the\innerrecurse}} + +\endTEX + +\def\redoloop + {\expandrecursecontent\endofloop} + +\def\nodoloop#1% + {\let\endofloop\dodoloop % new, permits nested \doloop's + \@EA\let\@EA\recurselevel\csname\@@irecurse\recursedepth\endcsname + \global\advance\outerrecurse\minusone} + +\def\exitloop % \exitloop quits at end + {\let\endofloop\nodoloop} + +\long\def\exitloopnow#1\endofloop % \exitloopnow quits directly + {\nodoloop} + +%D The loop is executed at least once, so beware of situations +%D like: +%D +%D \starttyping +%D \doloop {\exitloop some commands} +%D \stoptyping +%D +%D It's just a matter of putting the text into the \type{\if} +%D statement that should be there anyway, like in: +%D +%D \starttyping +%D \doloop {\ifwhatever \exitloop \else some commands\fi} +%D \stoptyping +%D +%D You can also quit a loop immediately, by using \type +%D {\exitloopnow} instead. Beware, this is more sensitive +%D for conditional errors. + +%D Krzysztof Leszczynski suggested to provide access to the level by +%D means of a \type {#1}. I decided to pass the more frquently used +%D level as \type {#1} and the less favoured depth as \type {#2}. The +%D intended usage is: +%D +%D \starttyping +%D \dorecurse{3}{\definesymbol[test-#1][xx-#1]} +%D +%D \def\test{\dorecurse{3}{\definesymbol[test-##1][xx-##1]}} \test +%D +%D \symbol[test-1]\quad\symbol[test-2]\quad\symbol[test-3] +%D \stoptyping +%D +%D Since the hashed arguments are expanded, we don't need tricky +%D expansion here. +%D +%D \starttyping +%D \dorecurse{3}{\expanded{\definesymbol[test-\recurselevel][xx-\recurselevel]}} +%D \stoptyping + +\def\expandrecursecontent + {\csname\@@arecurse\recursedepth\@EA\@EA\@EA\endcsname\@EA\@EA\@EA{\@EA\recurselevel\@EA}\@EA{\recursedepth}} + +\long\def\xdorecurse#1#2% + {\global\advance\outerrecurse \plusone + \long\global\@EA\def\csname\@@arecurse\recursedepth\endcsname##1##2{#2}% + \global\@EA\let\csname\@@irecurse\recursedepth\endcsname\recurselevel + \@EA\dodorecurse\@EA1\@EA{\number#1}} + +\long\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4 + {\global\advance\outerrecurse \plusone + \long\global\@EA\def\csname\@@arecurse\recursedepth\endcsname##1##2{#4}% + \global\@EA\let\csname\@@irecurse\recursedepth\endcsname\recurselevel + \ifnum#3>0\relax + \ifnum#2<#1\relax + \let\nextrecurse\exitstepwiserecurse + \else + \let\nextrecurse\dodostepwiserecurse + \fi + \else + \ifnum#3<0\relax + \ifnum#1<#2\relax + \let\nextrecurse\exitstepwiserecurse + \else + \let\nextrecurse\dodostepwisereverse + \fi + \else + \let\nextrecurse\exitstepwiserecurse + \fi + \fi\expanded{\nextrecurse{\number#1}{\number#2}{\number#3}}} + +\long\def\doloop#1% + {\global\advance\outerrecurse \plusone + \long\global\@EA\def\csname\@@arecurse\recursedepth\endcsname##1##2{#1}% + \global\@EA\let\csname\@@irecurse\recursedepth\endcsname\recurselevel + \let\endofloop\dodoloop + \dodoloop1} % no \plusone else \recurselevel wrong + +%D For special purposes: + +\newcount\fastrecursecounter +\newcount\lastrecursecounter +\newcount\steprecursecounter + +\def\dofastrecurse#1#2#3#4% + {\def\fastrecursebody{#4}% + \fastrecursecounter#1\relax + \lastrecursecounter#2\relax + \steprecursecounter#3\relax + \def\recurselevel{\number\fastrecursecounter}% + \dodofastrecurse} + +\def\dodofastrecurse + {\ifnum\fastrecursecounter>\lastrecursecounter\else + \fastrecursebody + \advance\fastrecursecounter\steprecursecounter + \expandafter\dodofastrecurse + \fi} + +%D This alternative looks a bit different and uses a +%D pseudo counter. When this macro is nested, we have to use +%D different counters. This time we use keywords. +%D +%D \starttyping +%D \def\alfa{2} \def\beta{100} \def\gamma{3} +%D +%D \for \n=55 \to 100 \step 1 \do {... \n ...} +%D \for \n=\alfa \to \beta \step \gamma \do {... \n ...} +%D \for \n=\n \to 120 \step 1 \do {... \n ...} +%D \for \n=120 \to 100 \step -3 \do {... \n ...} +%D \for \n=55 \to 100 \step 2 \do {... \n ...} +%D \stoptyping +%D +%D Only in the third example we need to predefine \type{\n}. +%D The use of \type{\od} as a dilimiter would have made nested +%D use more problematic. + +%D Don't use this one, it's kind of obsolete. + +\def\for#1=#2\to#3\step#4\do#5% + {\dostepwiserecurse{#2}{#3}{#4} + {\let#1\recurselevel#5\let#1\recurselevel}} + +%D \macros +%D {newevery,everyline,EveryLine,EveryPar} +%D +%D Lets skip to something quite different. It's common use +%D to use \type{\everypar} for special purposes. In \CONTEXT\ +%D we use this primitive for locating sidefloats. This means +%D that when user assignments to \type{\everypar} can interfere +%D with those of the package. We therefore introduce +%D \type{\EveryPar}. +%D +%D The same goes for \type{\EveryLine}. Because \TEX\ offers +%D no \type{\everyline} primitive, we have to call for +%D \type{\everyline} when we are working on a line by line +%D basis. Just by calling \type{\EveryPar{}} and +%D \type{\EveryLine{}} we restore the old situation. +%D +%D The definition command \type{\DoWithEvery} will be quite +%D unreadable, so let's first show an implementation that +%D shows how things are done: +%D +%D \starttyping +%D \newtoks \everyline +%D \newtoks \oldeveryline +%D \newif \ifeveryline +%D +%D \def\DoWithEvery#1#2#3#4% +%D {#3\else\edef\next{\noexpand#2={\the#1}}\next\fi +%D \edef\next{\noexpand#1={\the#2\the\scratchtoks}}\next +%D #4} +%D +%D \def\doEveryLine% +%D {\DoWithEvery\everyline\oldeveryline\ifeveryline\everylinetrue} +%D +%D \def\EveryLine% +%D {\afterassignment\doEveryLine\scratchtoks} +%D \stoptyping +%D +%D The real implementation is a bit more complicated but we +%D prefer something more versatile. + +% the old one +% +% \def\DoWithEvery#1% +% {\csname if\strippedcsname#1\endcsname \else +% \edef\next% +% {\@EA\noexpand\csname old\strippedcsname#1\endcsname= +% {\the#1}}% +% \next +% \fi +% \edef\next% +% {\noexpand#1= +% {\@EA\the\csname old\strippedcsname#1\endcsname\the\scratchtoks}}% +% \next +% \csname\strippedcsname#1true\endcsname} +% +% \def\dowithevery#1% +% {\@EA\afterassignment\csname do\strippedcsname#1\endcsname\scratchtoks} +% +% \def\newevery#1#2% +% {\ifx#1\undefined\newtoks#1\fi +% \ifx#2\relax\else\ifx#2\undefined +% \@EA\newtoks\csname old\strippedcsname#1\endcsname +% \@EA\newif \csname if\strippedcsname#1\endcsname +% \@EA\def \csname do\strippedcsname#2\endcsname{\DoWithEvery#1}% +% \def#2{\dowithevery#2}% +% \fi\fi} +% +% cleaner and more efficient + +%\def\dowithevery#1% +% {\def\dodowithevery% +% {\ifcase\csname c\strippedcsname#1\endcsname \expandafter\chardef +% \csname c\strippedcsname#1\endcsname=1 +% \csname t\strippedcsname#1\endcsname=#1% +% \fi +% \edef\next% +% {#1={\the\csname t\strippedcsname#1\endcsname\the\scratchtoks}}% +% \next}% +% \afterassignment\dodowithevery\scratchtoks} +% +% more efficient: + +\def\dodowithevery#1% + {\ifcase\csname c\strippedcsname#1\endcsname \expandafter\chardef + \csname c\strippedcsname#1\endcsname1 + \csname t\strippedcsname#1\endcsname#1% + \fi + \edef\next% + {#1{\the\csname t\strippedcsname#1\endcsname\the\scratchtoks}}% + \next} + +\def\dowithevery#1% + {\def\next{\dodowithevery#1}% + \afterassignment\next\scratchtoks} + +\bgroup \let\newtoks\relax % plain safe (\outer) + +\gdef\newevery#1#2% + {\ifx#1\undefined\csname newtoks\endcsname#1\fi % plain safe (\outer) + \ifx#2\relax\else\ifx#2\undefined + \expandafter\newtoks\csname t\strippedcsname#1\endcsname + \expandafter\chardef\csname c\strippedcsname#1\endcsname\zerocount + \def#2{\dowithevery#1}% + \fi\fi} + +\egroup + +%D The first \type {\outer} hack is needed to trick \TEX\ +%D into thinking that \type {\newtoks} is no outer macro, +%D the second hack is needed due to some funny interaction +%D between outer macros and \type {\if} at expansion time. + +%D This one permits definitions like: + +\newevery \everypar \EveryPar +\newevery \everyline \EveryLine + +%D and how about: + +\newevery \neverypar \NeveryPar + +%D Which we're going to use indeed! When the second argument +%D equals \type {\relax}, the first token list is created +%D unless it is already defined. + +%D Technically spoken we could have used the method we are +%D going to present in the visual debugger. First we save +%D the primitive \type{\everypar}: +%D +%D \starttyping +%D \let\normaleverypar=\everypar +%D \stoptyping +%D +%D Next we allocate a \TOKENLIST\ named \type{\everypar}, +%D which means that \type{\everypar} is no longer a primitive +%D but something like \type{\toks44}. +%D +%D \starttyping +%D \newtoks\everypar +%D \stoptyping +%D +%D Because \TEX\ now executes \type{\normaleverypar} instead +%D of \type{\everypar}, we are ready to assign some tokens to +%D this internally known and used \TOKENLIST. +%D +%D \starttyping +%D \normaleverypar={all the things the system wants to do \the\everypar} +%D \stoptyping +%D +%D Where the user can provide his own tokens to be expanded +%D every time he expects them to expand. +%D +%D \starttyping +%D \everypar={something the user wants to do} +%D \stoptyping +%D +%D We don't use this method because it undoubtly leads to +%D confusing situations, especially when other packages are +%D used, but it's this kind of tricks that make \TEX\ so +%D powerful. + +%D \macros +%D {convertargument,convertcommand,convertvalue} +%D +%D Some persistent experimenting led us to the next macro. This +%D macro converts a parameter or an expanded macro to it's +%D textual meaning. +%D +%D \starttyping +%D \convertargument ... \to \command +%D \stoptyping +%D +%D For example, +%D +%D \starttyping +%D \convertargument{one \two \three{four}}\to\ascii +%D \stoptyping +%D +%D The resulting macro \type{\ascii} can be written to a file +%D or the terminal without problems. In \CONTEXT\ we use this +%D macro for generating registers and tables of contents. +%D +%D The second conversion alternative accepts a command: +%D +%D \starttyping +%D \convertcommand\command\to\ascii +%D \stoptyping +%D +%D Both commands accept the prefix \type{\doglobal} for global +%D assignments. + +\beginTEX + +\def\doconvertargument#1>{} + +\def\convertedcommand + {\expandafter\doconvertargument\meaning} + +\long\def\convertargument#1\to#2% + {\long\def#2{#1}% saves a restore + \dodoglobal\edef#2{\convertedcommand#2}} + +\long\def\convertcommand#1\to#2% + {\dodoglobal\edef#2{\convertedcommand#1}} + +% no dodoglobal ! + +\long\def\defconvertedargument#1#2% less sensitive for \to + {\long\def#1{#2}% saves a restore + \edef#1{\convertedcommand#1}} + +\long\def\defconvertedcommand#1#2% less sensitive for \to + {\edef#1{\convertedcommand#2}} + +\long\def\gdefconvertedargument#1#2% less sensitive for \to + {\long\gdef#1{#2}% saves a restore + \xdef#1{\convertedcommand#1}} + +\long\def\gdefconvertedcommand#1#2% less sensitive for \to + {\xdef#1{\convertedcommand#2}} + +\endTEX + +\def\convertvalue#1\to + {\expandafter\convertcommand\csname#1\endcsname\to} + +\def\defconvertedvalue#1#2% less sensitive for \to + {\@EA\defconvertedcommand\@EA#1\csname#2\endcsname} + +%D \macros +%D {doifassignmentelse} +%D +%D A lot of \CONTEXT\ commands take optional arguments, for +%D instance: +%D +%D \starttyping +%D \dothisorthat[alfa,beta] +%D \dothisorthat[first=foo,second=bar] +%D \dothisorthat[alfa,beta][first=foo,second=bar] +%D \stoptyping +%D +%D Although a combined solution is possible, we prefer a +%D seperation. The next command takes care of propper +%D handling of such multi||faced commands. +%D +%D \starttyping +%D \doifassignmentelse {...} {then ...} {else ...} +%D \stoptyping + +% not robust +% +% \def\doifassignmentelse% +% {\doifinstringelse{=}} +% +% readable +% +% \def\doifassignmentelse#1% +% {\convertargument#1\to\ascii +% \doifinstringelse{=}{\ascii}} + +\def\doifassignmentelse#1% + {\convertargument#1\to\ascii + \doifinstringelse=\ascii} + +%D \macros +%D {convertasciiafter} +%D +%D Sometimes we need to convert an argument to a string (letters +%D only), for instance when we compare it with another string: +%D +%D \starttyping +%D \convertasciiafter\doifinstringelse{em}{\ascii}{...} +%D \stoptyping + +\def\convertasciiafter#1#2% + {\convertargument#2\to\asciiafter + \@EA#1\@EA{\asciiafter}} + +%D In \ETEX\ we can use \type {\detokenize} and gain some +%D speed, but in general far less that 1\% for \type +%D {\convertargument} and nil for \type {\convertcommand}. +%D This macro is more robust than the pure \TEX\ one, +%D something I found out when primitives like \type +%D {\jobname} were fed (or something undefined). + +% command variant: one level expansion ! + +\beginETEX \detokenize + +\long\def\convertargument#1\to#2{\dodoglobal\edef#2{\detokenize{#1}}} +\long\def\convertcommand #1\to#2{\dodoglobal\edef#2{\@EA\detokenize\@EA{#1}}} % hm, only second is also ok + +\long\def\defconvertedargument #1#2{\edef#1{\detokenize {#2}}} +\long\def\defconvertedcommand #1#2{\edef#1{\detokenize\@EA{#2}}} +\long\def\edefconvertedargument#1#2{\edef#1{#2}% + \edef#1{\detokenize\@EA{#1}}} +\long\def\gdefconvertedargument#1#2{\xdef#1{\detokenize {#2}}} +\long\def\gdefconvertedcommand #1#2{\xdef#1{\detokenize\@EA{#2}}} +\long\def\xdefconvertedargument#1#2{\xdef#1{#2}% + \xdef#1{\detokenize\@EA{#1}}} + +\endETEX + +%D When you try to convert a primitive command, you'll find +%D out that the \ETEX\ method fails on for instance \type +%D {\jobname} in the sense that it returns the filename +%D instead of just \type {\jobname}. So far this does not +%D give real problems. + +%D This is typically a macro that one comes to after reading +%D the \TEX book carefully. Even then, the definite solution +%D was found after rereading the \TEX book. The first +%D implementation was: +%D +%D \starttyping +%D \def\doconvertargument#1->#2\\\\{#2} +%D \stoptyping +%D +%D The \type{-}, the delimiter \type{\\\\} and the the second +%D argument are completely redundant. + +%D \macros +%D {showvalue,showargument} +%D +%D Two handy macros for testing purposes only: + +\def\showvalue#1% + {\expandafter\show\csname#1\endcsname} + +\beginETEX + +\def\showvalue#1% + {\ifcsname#1\endcsname + \expandafter\show\csname#1\endcsname + \else + \show\undefined + \fi} + +\endETEX + +\long\def\showargument#1% + {\defconvertedargument\ascii{#1}\ascii} + +%D \macros +%D {doifmeaningelse} +%D +%D We can use both commands in testing, but alas, not all +%D meanings expand to something \type {->}. This is no problem +%D in the \ETEX\ implementation, but since we want +%D compatibility, we need: +%D +%D \starttyping +%D \doifmeaningelse {\next} {\something} {true} {false} +%D \stoptyping +%D +%D Watch the one level expansion of the second argument. + +\def\doifmeaningelse#1#2% + {\edef\!!stringa{\meaning#1}% + \def\!!stringb{#2}\edef\!!stringb{\meaning\!!stringb}% + \ifx\!!stringa\!!stringb + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +%D \macros +%D {doifsamestringselse,doifsamestring,doifnotsamestring} +%D +%D The next comparison macro converts the arguments into +%D expanded strings. This command can be used to compare for +%D instance \type {\jobname} with a name stored in a macro. + +\def\@@doifsamestringelse#1#2% + {\edef\!!stringa{#1}% + \edef\!!stringb{#2}% + \convertcommand\!!stringa\to\!!stringa + \convertcommand\!!stringb\to\!!stringb + \ifx\!!stringa\!!stringb} + +\def\doifsamestringelse#1#2% + {\@@doifsamestringelse{#1}{#2}% + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +\def\doifsamestring#1#2% + {\@@doifsamestringelse{#1}{#2}% + \expandafter\firstofoneargument + \else + \expandafter\gobbleoneargument + \fi} + +\def\doifnotsamestring#1#2% + {\@@doifsamestringelse{#1}{#2}% + \expandafter\gobbleoneargument + \else + \expandafter\firstofoneargument + \fi} + +%D \macros +%D {ExpandFirstAfter,ExpandSecondAfter,ExpandBothAfter} +%D +%D These three commands support expansion of arguments before +%D executing the commands that uses them. We can best +%D illustrate this with an example. +%D +%D \starttyping +%D \def\first {alfa,beta,gamma} +%D \def\second {alfa,epsilon,zeta} +%D +%D \ExpandFirstAfter \doifcommon {\first} {alfa} {\message{OK}} +%D \ExpandSecondAfter \doifcommon {alfa} {\second} {\message{OK}} +%D \ExpandBothAfter \doifcommon {\first} {\second} {\message{OK}} +%D +%D \ExpandFirstAfter\processcommalist[\first]\message +%D +%D \ExpandAfter \doifcommon {\first} {alfa} {\message{OK}} +%D \stoptyping +%D +%D The first three calls result in the threefold message +%D \type{OK}, the fourth one shows the three elements of +%D \type{\first}. The command \type{\ExpandFirstAfter} takes +%D care of (first) arguments that are delimited by \type{[ ]}, +%D but the faster \type{\ExpandAfter} does not. + +\def\simpleExpandFirstAfter#1% + {\long\xdef\@@expanded{\noexpand\ExpandCommand{#1}}\@@expanded} + +\def\complexExpandFirstAfter[#1]% + {\long\xdef\@@expanded{\noexpand\ExpandCommand[#1]}\@@expanded} + +\def\ExpandFirstAfter#1% + {\let\ExpandCommand#1% + \doifnextoptionalelse\complexExpandFirstAfter\simpleExpandFirstAfter} + +\def\ExpandSecondAfter#1#2#3% + {\scratchtoks{#2}% + \long\xdef\@@expanded{\noexpand#1{\the\scratchtoks}{#3}}\@@expanded} + +\def\ExpandBothAfter#1#2#3% + {\long\xdef\@@expanded{\noexpand#1{#2}{#3}}\@@expanded} + +\def\ExpandAfter#1#2% + {\long\xdef\@@expanded{\noexpand#1{#2}}\@@expanded} + +%D Now we can for instance define \type{\ifinstringelse} as: + +\def\ifinstringelse + {\ExpandBothAfter\p!doifinstringelse} + +%D \macros +%D {ConvertToConstant,ConvertConstantAfter} +%D +%D When comparing arguments with a constant, we can get into +%D trouble when this argument consists of tricky expandable +%D commands. One solution for this is converting the +%D argument to a string of unexpandable characters. To make +%D comparison possible, we have to convert the constant too +%D +%D \starttyping +%D \ConvertToConstant\doifelse {...} {...} {then ...} {else ...} +%D \stoptyping +%D +%D This construction is only needed when the first argument +%D can give troubles. Misuse can slow down processing. +%D +%D \starttyping +%D \ConvertToConstant\doifelse{\c!alfa} {\c!alfa}{...}{...} +%D \ConvertToConstant\doifelse{alfa} {\c!alfa}{...}{...} +%D \ConvertToConstant\doifelse{alfa} {alfa} {...}{...} +%D \ConvertToConstant\doifelse{alfa \alfa test}{\c!alfa}{...}{...} +%D \stoptyping +%D +%D In examples~2 and~3 both arguments equal, in~1 and~4 +%D they differ. + +\beginTEX + +\long\def\ConvertToConstant#1#2#3% + {\expandafter\defconvertedargument\expandafter\!!stringa\expandafter{#2}% + \expandafter\defconvertedargument\expandafter\!!stringb\expandafter{#3}% + #1{\!!stringa}{\!!stringb}} + +\endTEX + +\beginETEX \detokenize + +\long\def\ConvertToConstant#1#2#3% + {\edef\!!stringa{\expandafter\detokenize\expandafter{#2}}% + \edef\!!stringb{\expandafter\detokenize\expandafter{#3}}% + #1{\!!stringa}{\!!stringb}} + +\endETEX + +%D When the argument \type{#1} consists of commands, we had +%D better use +%D +%D \starttyping +%D \ConvertConstantAfter\processaction[#1][...] +%D \ConvertConstantAfter\doifelse{#1}{\v!something}{}{} +%D \stoptyping +%D +%D This commands accepts things like: +%D +%D \starttyping +%D \v!constant +%D constant +%D \hbox to \hsize{\rubish} +%D \stoptyping +%D +%D As we will see in the core modules, this macro permits +%D constructions like: +%D +%D \starttyping +%D \setupfootertexts[...][...] +%D \setupfootertexts[margin][...][...] +%D \setupfootertexts[\v!margin][...][...] +%D \stoptyping +%D +%D where \type{...} can be anything legally \TEX. + +\def\CheckConstantAfter#1#2% + {\@EA\convertargument\v!prefix!\to\ascii + \convertargument#1\to#2\relax + \doifinstringelse\ascii{#2} + {\expandafter\convertargument#1\to#2} + {}} + +\def\ConvertConstantAfter#1#2#3% + {\CheckConstantAfter{#2}\asciia + \CheckConstantAfter{#3}\asciib + #1{\asciia}{\asciib}} + +%D \macros +%D {assignifempty} +%D +%D We can assign a default value to an empty macro using: +%D +%D \starttyping +%D \assignifempty \macros {default value} +%D \stoptyping +%D +%D We don't explicitly test if the macro is defined. + +\def\assignifempty#1#2% can be sped up + {\doifsomething{#1}{\def#1{#2}}} % {\doifnot{#1}{}{\def#1{#2}}} + +%D \macros +%D {gobbleuntil,grabuntil,gobbleuntilrelax, +%D processbetween,processuntil} +%D +%D In \TEX\ gobbling usually stand for skipping arguments, so +%D here are our gobbling macros. +%D +%D In \CONTEXT\ we use a lot of \type{\start}||\type{\stop} +%D like constructions. Sometimes, the \type{\stop} is used as a +%D hard coded delimiter like in: +%D +%D \starttyping +%D \def\startcommand#1\stopcommand% +%D {... #1 ...} +%D \stoptyping +%D +%D In many cases the \type{\start}||\type{\stop} pair is +%D defined at format generation time or during a job. This +%D means that we cannot hardcode the \type{\stop} criterium. +%D Only after completely understanding \type{\csname} and +%D \type{\expandafter} I was able to to implement a solution, +%D starting with: +%D +%D \starttyping +%D \grabuntil{stop}\command +%D \stoptyping +%D +%D This commands executes, after having encountered +%D \type {\stop} the command \type {\command}. This command +%D receives as argument the text preceding the \type {\stop}. +%D This means that: +%D +%D \starttyping +%D \def\starthello% +%D {\grabuntil{stophello}\message} +%D +%D \starthello Hello world!\stophello +%D \stoptyping +%D +%D results in: \type{\message{Hello world!}}. + +\def\dograbuntil#1#2% + {\long\def\next##1#1{#2{##1}}\next} + +\def\grabuntil#1% + {\expandafter\dograbuntil\expandafter{\csname#1\endcsname}} + +%D The next command build on this mechanism: +%D +%D \starttyping +%D \processbetween{string}\command +%D \stoptyping +%D +%D Here: +%D +%D \starttyping +%D \processbetween{hello}\message +%D \starthello Hello again!\stophello +%D \stoptyping +%D +%D leads to: \type{\message{Hello again!}}. The command +%D +%D \starttyping +%D \gobbleuntil{sequence} +%D \stoptyping +%D +%D is related to these commands. This one simply throws away +%D everything preceding \type{\command}. + +\long\def\processbetween#1#2% + {\setvalue{\s!start#1}{\grabuntil{\s!stop#1}{#2}}} + +\def\gobbleuntil#1% + {\long\def\next##1#1{}\next} + +\def\gobbleuntilrelax#1\relax + {} + +%D The next one simply expands the pickup up tokens. +%D +%D \starttyping +%D \processuntil{sequence} +%D \stoptyping + +\def\processuntil#1% + {\long\def\next##1#1{##1}\next} + +%D \macros +%D {groupedcommand} +%D +%D Commands often manipulate argument as in: +%D +%D \starttyping +%D \def\doezomaarwat#1{....#1....} +%D \stoptyping +%D +%D A disadvantage of this approach is that the tokens that +%D form \type{#1} are fixed the the moment the argument is read +%D in. Normally this is no problem, but for instance verbatim +%D environments adapt the \CATCODES\ of characters and therefore +%D are not always happy with already fixed tokens. +%D +%D Another problem arises when the argument is grouped not by +%D \type{{}} but by \type{\bgroup} and \type{\egroup}. Such an +%D argument fails, because the \type{\bgroup} is een as the +%D argument (which is quite normal). +%D +%D The next macro offers a solution for both unwanted +%D situations: +%D +%D \starttyping +%D \groupedcommand {before} {after} +%D \stoptyping +%D +%D Which can be used like: +%D +%D \starttyping +%D \def\cite% +%D {\groupedcommand{\rightquote\rightquote}{\leftquote\leftquote}} +%D \stoptyping +%D +%D This command is equivalent to, but more 'robust' than: +%D +%D \starttyping +%D \def\cite#1% +%D {\rightquote\rightquote#1\leftquote\leftquote} +%D \stoptyping +%D +%D One should say that the next implementation would suffice: +%D +%D \starttyping +%D \def\groupedcommand#1#2% +%D {\def\BeforeGroup{#1\ignorespaces}% +%D \def\AfterGroup{\unskip#2\egroup}% +%D \bgroup\bgroup +%D \aftergroup\AfterGroup +%D \afterassignment\BeforeGroup +%D \let\next=} +%D \stoptyping +%D +%D It did indeed, but one day we decided to support the +%D processing of boxes too: +%D +%D \starttyping +%D \def\rightword% +%D {\groupedcommand{\hfill\hbox}{\parfillskip\!!zeropoint}} +%D +%D .......... \rightword{the right way} +%D \stoptyping +%D +%D Here \TEX\ typesets \type{\bf the right way} unbreakable +%D at the end of the line. The solution mentioned before does +%D not work here. +%D +%D \starttyping +%D \long\unexpanded\def\groupedcommand#1#2% +%D {\bgroup +%D \long\def\BeforeGroup% +%D {\bgroup#1\bgroup\aftergroup\AfterGroup}% +%D \long\def\AfterGroup% +%D {#2\egroup\egroup}% +%D \afterassignment\BeforeGroup +%D \let\next=} +%D \stoptyping +%D +%D We used this method some time until the next alternative +%D was needed. From now on we support both +%D +%D \starttyping +%D to be \bold{bold} or not, that's the question +%D \stoptyping +%D +%D and +%D +%D \starttyping +%D to be {\bold bold} or not, that's the question +%D \stoptyping +%D +%D This alternative checks for a \type{\bgroup} token first. +%D The internal alternative does not accept the box handling +%D mentioned before, but further nesting works all right. The +%D extra \type{\bgroup}||\type{\egroup} is needed to keep +%D \type{\AfterGroup} both into sight and local. + +\long\def\HandleGroup#1#2% + {\bgroup + \long\def\BeforeGroup{\bgroup#1\bgroup\aftergroup\AfterGroup}% + \long\def\AfterGroup {#2\egroup\egroup}% + \afterassignment\BeforeGroup + \let\next=} + +\long\def\HandleSimpleGroup#1#2% no inner group (so no kerning interference) + {\bgroup + %long\def\BeforeGroup{\bgroup#1\aftergroup\AfterGroup}% interferes + \long\def\BeforeGroup{\bgroup\aftergroup\AfterGroup#1}% + \long\def\AfterGroup {#2\egroup}% + \afterassignment\BeforeGroup + \let\next=} + +\long\def\HandleNoGroup#1#2% + {\long\def\AfterGroup{#2\egroup}% + \bgroup\aftergroup\AfterGroup#1} + +%D These macros come together in: +%D +%D \starttyping +%D \long\unexpanded\def\groupedcommand#1#2% +%D {\def\dogroupedcommand% +%D {\ifx\next\bgroup +%D \let\next=\HandleGroup +%D \else +%D \let\next=\HandleNoGroup +%D \fi +%D \next{#1}{#2}}% +%D \futurelet\next\dogroupedcommand} +%D \stoptyping +%D +%D From the missing paragraph number one can deduce that the +%D last macro is not the real one yet. I considered it a +%D nuisance that +%D +%D \starttyping +%D \color[green] +%D {as grass} +%D \stoptyping +%D +%D was not interpreted as one would expect. This is due to the +%D fact that \type{\futurelet} obeys blank spaces, and a +%D line||ending token is treated as a blank space. So the final +%D implementation became: + +%\long\unexpanded\def\groupedcommand#1#2% +% {\bgroup +% \def\dogroupedcommand% +% {\ifx\next\bgroup +% \def\\{\egroup\HandleGroup{#1}{#2}}% +% \else\ifx\next\blankspace +% \def\\ {\egroup\groupedcommand{#1}{#2}}% +% \else +% \def\\{\egroup\HandleNoGroup{#1}{#2}}% +% \fi\fi +% \\}% +% \futurelet\next\dogroupedcommand} +% +% compatible ? + +\long\unexpanded\def\groupedcommand#1#2% + {\doifnextbgroupelse{\HandleGroup{#1}{#2}}{\HandleNoGroup{#1}{#2}}} + +\long\unexpanded\def\simplegroupedcommand#1#2% + {\doifnextbgroupelse{\HandleSimpleGroup{#1}{#2}}{\HandleNoGroup{#1}{#2}}} + +%D Users should be aware of the fact that grouping can +%D interfere with ones paragraph settings that are executed +%D after the paragraph is closed. One should therefore +%D explictly close the paragraph with \type{\par}, else the +%D settings will be forgotten and not applied. So it's: +%D +%D \starttyping +%D \def\BoldRaggedCenter% +%D {\groupedcommand{\raggedcenter\bf}{\par}} +%D \stoptyping + +%D \macros +%D {checkdefined} +%D +%D The bigger the system, the greater the change that +%D user defined commands collide with those that are part of +%D the system. The next macro gives a warning when a command is +%D already defined. We considered blocking the definition, but +%D this is not always what we want. +%D +%D \starttyping +%D \checkdefined {category} {class} {command} +%D \stoptyping +%D +%D The user is warned with the suggestion to use +%D \type{CAPITALS}. This suggestion is feasible, because +%D \CONTEXT only defines lowcased macros. + +\def\showdefinederror#1#2% + {\writestatus\m!systems{#1 #2 replaces a macro, use CAPITALS!}} + +\def\checkdefined#1#2#3% + {\doifdefined{#3}{\showdefinederror{#2}{#3}}} + +%D \macros +%D {GotoPar,GetPar} +%D +%D Typesetting a paragraph in a special way can be done by +%D first grabbing the contents of the paragraph and processing +%D this contents grouped. The next macro for instance typesets +%D a paragraph in boldface. +%D +%D \starttyping +%D \def\remark#1\par% +%D {\bgroup\bf#1\egroup} +%D \stoptyping +%D +%D This macro has to be called like +%D +%D \starttyping +%D \remark some text ... ending with \par +%D \stoptyping +%D +%D Instead of \type{\par} we can of course use an empty line. +%D When we started typesetting with \TEX, we already had +%D produced lots of text in plain \ASCII. In producing such +%D simple formatted texts, we adopted an open layout, and when +%D switching to \TEX, we continued this open habit. Although +%D \TEX\ permits a cramped and badly formatted source, it adds +%D to confusion and sometimes introduces errors. So we prefer: +%D +%D \starttyping +%D \remark +%D +%D some text ... ending with an empty line +%D \stoptyping +%D +%D We are going to implement a mechanism that allows such open +%D specifications. The definition of the macro handling +%D \type{\remark} becomes: +%D +%D \starttyping +%D \def\remark% +%D {\BeforePar{\bgroup\bf}% +%D \AfterPar{\egroup}% +%D \GetPar} +%D \stoptyping +%D +%D A macro like \type{\GetPar} can be defined in several +%D ways. The recent version, the fourth one in a row, +%D originally was far more complicated, but some functionality +%D has been moved to other macros. +%D +%D We start with the more simple but in some cases more +%D appropriate alternative is \type{\GotoPar}. This one leaves +%D \type{\par} unchanged and is therefore more robust. On the +%D other hand, \type{\AfterPar} is not supported. + +\newtoks\BeforePar +\newtoks\AfterPar + +\let\endoflinetoken=^^M + +%D The original definition was: +%D +%D \starttyping +%D \def\doGotoPar +%D {\ifx\nextchar\blankspace +%D \@EA\GotoPar +%D \else\ifx\nextchar\endoflinetoken +%D \@EAEAEA\GotoPar +%D \else +%D \@EAEAEA\dodoGotoPar +%D \fi\fi} +%D +%D \def\dodoGotoPar +%D {\the\BeforePar +%D \BeforePar\emptytoks +%D \nextchar} +%D +%D \def\GotoPar +%D {\afterassignment\doGotoPar\let\nextchar=} +%D \stoptyping + +%D Its big brother \type{\GetPar} redefines the \type{\par} +%D primitive, which can lead to unexpected results, depending +%D in the context. +%D +%D \starttyping +%D \def\GetPar +%D {\expanded +%D {\BeforePar +%D {\the\BeforePar +%D \BeforePar\emptytoks +%D \bgroup +%D \def\par +%D {\egroup +%D \par +%D \the\AfterPar +%D \BeforePar\emptytoks +%D \AfterPar\emptytoks}}}% +%D \GotoPar} +%D \stoptyping + +%D However, we can implement a better alternative by using: +%D +%D \starttyping +%D \def\dowithpar#1#2% +%D {\def\handlepar##1\par{#1##1#2}% +%D \def\gobblepar\par{\dowithpar{#1}{#2}}% +%D \doifnextcharelse\par\gobblepar\handlepar} +%D \stoptyping +%D +%D Or, nicer + +\def\redowithpar\par + {\doifnextcharelse\par\redowithpar\dodowithpar}% + +\def\dowithpar#1#2% + {\def\dodowithpar##1\par{#1##1#2}% + \redowithpar\par} + +\def\redogotopar\par + {\doifnextcharelse\par\redogotopar\dodogotopar}% + +\def\dogotopar#1% + {\def\dodogotopar{#1}% + \redogotopar\par} + +%D The previosuly defined macros now become: + +\def\GetPar + {\expanded + {\dowithpar + {\the\BeforePar + \BeforePar\emptytoks} + {\the\AfterPar + \BeforePar\emptytoks + \AfterPar\emptytoks}}} + +\def\GotoPar + {\expanded + {\dogotopar + {\the\BeforePar + \BeforePar\emptytoks}}} + +%D \macros +%D {dowithpargument,dowithwargument} +%D +%D The next macros are a variation on \type{\GetPar}. When +%D macros expect an argument, it interprets a grouped sequence +%D of characters a one token. While this adds to robustness and +%D less ambiguous situations, we sometimes want to be a bit +%D more flexible, or at least want to be a bit more tolerant +%D to user input. +%D +%D We start with a commands that acts on paragraphs. This +%D command is called as: +%D +%D \starttyping +%D \dowithpargument\command +%D \dowithpargument{\command ... } +%D \stoptyping +%D +%D In \CONTEXT\ we use this one to read in the titles of +%D chapters, sections etc. The commands responsible for these +%D activities accept several alternative ways of argument +%D passing. In these examples, the \type{\par} can be omitted +%D when an empty line is present. +%D +%D \starttyping +%D \command{...} +%D \command ... \par +%D \command +%D {...} +%D \command +%D ... \par +%D \stoptyping +%D +%D We show two implementations, of which for the moment the +%D we prefier to use the second one: +%D +%D \starttyping +%D \def\dowithpargument#1% +%D {\def\dodowithpargument% +%D {\ifx\next\bgroup +%D \def\next{#1}% +%D \else +%D \def\next####1 \par{#1{####1}}% +%D \fi +%D \next}% +%D \futurelet\next\dodowithpargument} +%D \stoptyping +%D +%D A second and better implementation was: +%D +%D \starttyping +%D \def\dowithpargument#1% +%D {\def\nextpar##1 \par{#1{##1}}% +%D \def\nextarg##1{#1{##1}}% +%D \doifnextcharelse\bgroup +%D {\nextarg} +%D {\nextpar}} +%D \stoptyping +%D +%D We ended up with an alternative that also accepts en empty +%D argument. This command permits for instance chapters to +%D have no title. + +%\def\dowithpargument#1% +% {\def\nextpar##1 \par{#1{##1}}% +% \def\nextarg##1{#1{##1}}% +% \doifnextcharelse\bgroup +% {\nextarg} +% {\doifnextcharelse{\par} +% {#1{}} +% {\nextpar}}} + +\def\dowithpargument#1% + {\def\nextpar##1 \par{#1{##1}}% + \def\nextarg##1{#1{##1}}% + \doifnextbgroupelse\nextarg{\doifnextcharelse\par{#1{}}\nextpar}} + +%D The \type{p} in the previous command stands for paragraph. +%D When we want to act upon words we can use the \type{w} +%D alternative. +%D +%D \starttyping +%D \dowithwargument\command +%D \dowithwargument{... \command ...} +%D \stoptyping +%D +%D The main difference bwteen two alternatives is in the +%D handling of \type{\par}'s. This time the space token acts +%D as a delimiter. +%D +%D \starttyping +%D \command{...} +%D \command ... +%D \command +%D {...} +%D \command +%D ... +%D \stoptyping +%D +%D Again there are two implementations possible: +%D +%D \starttyping +%D \def\dowithwargument#1% +%D {\def\dodowithwargument% +%D {\ifx\next\bgroup +%D \def\next{#1}% +%D \else +%D \def\next####1 {#1{####1}}% +%D \fi +%D \next}% +%D \futurelet\next\dodowithwargument} +%D \stoptyping +%D +%D We've chosen: + +%\def\dowithwargument#1% +% {\def\nextwar##1 {#1{##1}}% +% \def\nextarg##1{#1{##1}}% +% \doifnextcharelse\bgroup +% {\nextarg} +% {\nextwar}} + +\def\dowithwargument#1% + {\def\nextwar##1 {#1{##1}}% + \def\nextarg##1{#1{##1}}% + \doifnextbgroupelse\nextarg\nextwar} + +%D \macros +%D {dorepeat,dorepeatwithcommand} +%D +%D When doing repetitive tasks, we stromgly advice to use +%D \type{\dorecurse}. The next alternative however, suits +%D better some of the \CONTEXT\ interface commands. +%D +%D \starttyping +%D \dorepeat[n*\command] +%D \stoptyping +%D +%D The value of the used \COUNTER\ can be called within +%D \type{\command} by \type{\repeater}. +%D +%D A slightly different alternative is: +%D +%D \starttyping +%D \dorepeatwithcommand[n*{...}]\command +%D \stoptyping +%D +%D When we call for something like: +%D +%D \starttyping +%D \dorepeatwithcommand[3*{Hello}]\message +%D \stoptyping +%D +%D we get ourselves three \type{\message{Hello}} messages in +%D a row. In both commands, the \type{n*} is optional. When this +%D specification is missing, the command executes once. + +% this one is obsolete: + +\def\dorepeat[#1]% + {\dodorepeat#1*\empty*\relax} + +\long\def\dodorepeat#1*#2#3*#4\relax + {\ifx#2\empty#1\else\dorecurse{#1}{#2#3}\fi} + +\def\repeater + {\recurselevel} + +% this one will be kept + +\def\dorepeatwithcommand[#1]% + {\dodorepeatwithcommand#1*\empty*\relax} + +% \long\def\dodorepeatwithcommand#1*#2#3*#4\relax#5% +% {\ifx#2\empty +% #5{#1}% +% \else +% \dorecurse{#1}{#5{#2#3}}% +% \fi} +% +% more complex but better: + +% \long\def\dodorepeatwithcommand#1*#2#3*#4\relax#5% +% {\ifx#2\empty +% #5{#1}% +% \else\ifnum#1<\zerocount +% % a la etex +% % \dorecurse{-\numexpr(#1)}{#5{-#2#3}}% +% % indirect +% %\innerrecurse#1% +% %\expanded{\dorecurse{\number-\innerrecurse}}{#5{-#2#3}}% +% % safer: +% \bgroup\scratchcounter#1% +% \expanded{\egroup\noexpand\dorecurse{\number-\scratchcounter}}{#5{-#2#3}}% +% \else\ifx#2+% +% \dorecurse{#1}{#5{#3}}% +% \else +% \dorecurse{#1}{#5{#2#3}}% +% \fi\fi\fi} + +\def\dorepeatwithcommand[#1]% + {\dodorepeatwithcommand#1*\empty*\relax} + +\long\def\dodorepeatwithcommand#1*#2#3*#4\relax#5% + {\ifx#2\empty\redorepeatwithcommand[#1]#5\else\dododorepeatwithcommand{#1}{#2}{#3}#5\fi} + +\long\def\dododorepeatwithcommand#1#2#3#4% + {\ifx#2\empty % redundant but gives cleaner extensions + #4{#1}% + \else\ifnum#1<\zerocount + \bgroup\scratchcounter#1% + \expanded{\egroup\noexpand\dorecurse{\number-\scratchcounter}}{#4{-#2#3}}% + \else\ifx#2+% + \dorecurse{#1}{#4{#3}}% + \else + \dorecurse{#1}{#4{#2#3}}% + \fi\fi\fi} + +\def\redorepeatwithcommand[#1]#2% + {#2{#1}} + +%D The extension hook permits something like: +%D +%D \starttyping +%D \bgroup +%D +%D \catcode`\*=\@@superscript +%D +%D \gdef\redorepeatwithcommand[#1]% +%D {\redodorepeatwithcommand#1*\empty*\relax} +%D +%D \long\gdef\redodorepeatwithcommand#1*#2#3*#4\relax#5% +%D {\dododorepeatwithcommand{#1}{#2}{#3}#5} +%D +%D \egroup +%D \stoptyping +%D +%D although one may wonder if changing the catcode of \type {*} is wise. + +%D \macros +%D {normalbgroup,normalgroup} +%D +%D No comment. + +\let\normalbgroup\bgroup +\let\normalegroup\egroup + +%D \macros +%D {doifstringinstringelse} +%D +%D The next macro is meant for situations where both strings +%D are macros. This save some unneeded expansion. +%D +%D \starttyping +%D \long\def\doifstringinstringelse#1#2% +%D {\p!doifinstringelse#1#2% +%D \@EA\firstoftwoarguments +%D \else +%D \@EA\secondoftwoarguments +%D \fi} +%D \stoptyping +%D +%D A bit faster is: + +\def\pp!doifstringinstringelse#1% + {\if#1@% + \@EA\secondoftwoarguments + \else + \@EA\firstoftwoarguments + \fi} + +\long\def\doifstringinstringelse#1#2% + {\long\@EA\def\@EA\p!doifstringinstringelse\@EA##\@EA1#1##2##3\war + {\pp!doifstringinstringelse##2}% + \@EA\@EA\@EA\p!doifstringinstringelse\@EA#2#1@@\war} + +%D \macros +%D {appendtoks,prependtoks,appendtoksonce,prependtoksonce, +%D doifintokselse,flushtoks,dotoks} +%D +%D We use \TOKENLISTS\ sparsely within \CONTEXT, because the +%D comma separated lists are more suitable for the user +%D interface. Nevertheless we have: +%D +%D \starttyping +%D (\doglobal) \appendtoks ... \to\tokenlist +%D (\doglobal) \prependtoks ... \to\tokenlist +%D (\doglobal) \flushtoks\tokenlist +%D \dotoks\tokenlist +%D \stoptyping +%D +%D Er worden eerst enkele klad||registers gedefinieerd. These +%D macros are clones of the ones implemented in page~378 of +%D Knuth's \TeX book. +%D +%D A simple implementation, one that does not handle braces +%D at the outer level, is: +%D +%D \starttyping +%D \def\appendtoks#1\to#2% +%D {\scratchtoks={#1}% +%D \expanded{\dodoglobal\noexpand#2{\the#2\the\scratchtoks}}} +%D +%D \def\prependtoks#1\to#2% +%D {\scratchtoks={#1}% +%D \expanded{\dodoglobal\noexpand#2{\the\scratchtoks\the#2}}} +%D \stoptyping +%D +%D But here we prefer: + +\newtoks\@@scratchtoks + +% before we had the once only alternatives, we had: +% +% \def\appendtoks {\doappendtoks \relax} +% \def\prependtoks{\doprependtoks\relax} +% +% \long\def\doappendtoks#1\to#2% +% {\@@scratchtoks\@EA{\gobbleoneargument#1}% +% \expanded{\dodoglobal\noexpand#2{\the#2\the\@@scratchtoks}}} +% +% \long\def\doprependtoks#1\to#2% +% {\@@scratchtoks\@EA{\gobbleoneargument#1}% +% \expanded{\dodoglobal\noexpand#2{\the\@@scratchtoks\the#2}}} + +\def\appendtoks {\doappendtoks \relax} +\def\prependtoks {\doprependtoks \relax} +\def\appendtoksonce {\doappendtoksonce \relax} +\def\prependtoksonce{\doprependtoksonce\relax} + +% \def\dodoappendtoks#1% +% {\expanded{\dodoglobal\noexpand#1{\the#1\the\@@scratchtoks}}} +% +% \def\dodoprependtoks#1% +% {\expanded{\dodoglobal\noexpand#1{\the\@@scratchtoks\the#1}}} +% +% \long\def\doappendtoks#1\to% +% {\@@scratchtoks\@EA{\gobbleoneargument#1}\dodoappendtoks} +% +% \long\def\doprependtoks#1\to% +% {\@@scratchtoks\@EA{\gobbleoneargument#1}\dodoprependtoks} +% +% \long\def\doappendtoksonce#1\to#2% +% {\@@scratchtoks\@EA{\gobbleoneargument#1}% +% \doifintokselse\@@scratchtoks{#2}{}{\dodoappendtoks{#2}}} +% +% \long\def\doprependtoksonce#1\to#2% +% {\@@scratchtoks\@EA{\gobbleoneargument#1}% +% \doifintokselse\@@scratchtoks{#2}{}{\dodoprependtoks{#2}}} +% +% A slightly (but in the case of large arguments +% significantly) faster alternative is given below: + +\newtoks\@@toks + +\def\dodoappendtoks + {\dodoglobal\@@toks\@EAEAEA{\@EA\the\@EA\@@toks\the\@@scratchtoks}} + +\def\dodoprependtoks + {\dodoglobal\@@toks\@EAEAEA{\@EA\the\@EA\@@scratchtoks\the\@@toks}} + +\long\def\doappendtoks#1\to#2% + {\def\@@toks{#2}% + \@@scratchtoks\@EA{\gobbleoneargument#1}\dodoappendtoks} + +\long\def\doprependtoks#1\to#2% + {\def\@@toks{#2}% + \@@scratchtoks\@EA{\gobbleoneargument#1}\dodoprependtoks} + +\long\def\doappendtoksonce#1\to#2% + {\def\@@toks{#2}% + \@@scratchtoks\@EA{\gobbleoneargument#1}% + \doifintokselse\@@scratchtoks\@@toks\donothing\dodoappendtoks} + +\long\def\doprependtoksonce#1\to#2% + {\def\@@toks{#2}% + \@@scratchtoks\@EA{\gobbleoneargument#1}% + \doifintokselse\@@scratchtoks\@@toks\donothing\dodoprependtoks} + +%D The test macro: +%D +%D \starttyping +%D \def\doifintokselse#1#2% #1 en #2 zijn toks +%D {\edef\!!stringa{\the#1}\convertcommand\!!stringa\to\asciia +%D \edef\!!stringb{\the#2}\convertcommand\!!stringb\to\asciib +%D \doifinstringelse\asciia\asciib} +%D \stoptyping +%D +%D Better: +%D +%D \starttyping +%D \def\doifintokselse#1#2% #1 en #2 zijn toks +%D {\edef\!!stringa{\the#1}\convertcommand\!!stringa\to\asciia +%D \edef\!!stringb{\the#2}\convertcommand\!!stringb\to\asciib +%D \doifstringinstringelse\asciia\asciib} +%D \stoptyping +%D +%D Even better: + +\beginETEX + + \def\doifintokselse#1#2% #1 en #2 zijn toks + {\@EA\convertargument\the#1\to\asciia + \@EA\convertargument\the#2\to\asciib + \doifstringinstringelse\asciia\asciib} + +\endETEX + +\beginTEX + + \def\doifintokselse#1#2% #1 en #2 zijn toks + {\edef\asciia{\the#1}\convertcommand\asciia\to\asciia + \edef\asciib{\the#2}\convertcommand\asciib\to\asciib + \doifstringinstringelse\asciia\asciib} + +\endTEX + +%D Also: + +\def\appendetoks #1\to{\expanded{\appendtoks #1}\to} +\def\prependetoks#1\to{\expanded{\prependtoks#1}\to} + +%D Hm. + +\def\flushtoks#1% nb: can reassing to #1 again, hence the indirectness + {\@@scratchtoks#1\relax + \dodoglobal#1\emptytoks + \the\@@scratchtoks\relax} + +\let\dotoks\the + +%D \macros +%D {makecounter,pluscounter,minuscounter, +%D resetcounter,setcounter,countervalue} +%D +%D Declaring, setting and resetting \COUNTERS\ can be doen +%D with the next set of commands. +%D +%D \starttyping +%D \makecounter {name} +%D \pluscounter {name} +%D \minuscounter {name} +%D \resetcounter {name} +%D \setcounter {name} {value} +%D \countervalue {name} +%D \stoptyping +%D +%D We prefer the use of global counters. This means that we +%D have to load \PLAIN\ \TEX\ in a bit different way: +%D +%D \starttyping +%D \let\oldouter=\outer +%D \let\outer=\relax +%D \input plain.tex +%D \let\outer=\oldouter +%D +%D \def\newcount% +%D {\alloc@0\count\countdef\insc@unt} +%D \stoptyping +%D +%D First we show a solution in which we use real \COUNTERS. +%D Apart from some expansion, nothing special is done. +%D +%D \starttyping +%D \def\makecounter#1% +%D {\expandafter\newcount\csname#1\endcsname} +%D +%D \def\pluscounter#1% +%D {\global\advance\csname#1\endcsname by 1 } +%D +%D \def\minuscounter#1% +%D {\global\advance\csname#1\endcsname by -1 } +%D +%D \def\resetcounter#1% +%D {\expandafter\global\csname#1\endcsname=0 } +%D +%D \def\setcounter#1#2% +%D {\expandafter\global\csname#1\endcsname=#2 } +%D +%D \def\countervalue#1% +%D {\the\getvalue{#1}} +%D \stoptyping +%D +%D Because these macros are already an indirect way of working +%D with counters, there is no harm in using pseudo \COUNTERS\ +%D here: + +\def\makecounter#1% + {\letgvalue{#1}\zerocountervalue} % see earlier + +% \def\countervalue#1% +% {\getvalue{#1}} + +\let\countervalue\getvalue + +\def\pluscounter#1% + {\scratchcounter\getvalue{#1}% + \advance\scratchcounter \plusone + \setxvalue{#1}{\the\scratchcounter}} + +\def\minuscounter#1% + {\scratchcounter\getvalue{#1}% + \advance\scratchcounter \minusone + \setxvalue{#1}{\the\scratchcounter}} + +\def\resetcounter#1% + {\letgvalue{#1}\zerocountervalue} + +\def\setcounter#1#2% or: \setxvalue{#1}{\number#2} + {\scratchcounter#2% + \setxvalue{#1}{\the\scratchcounter}} + +\def\incrementcounter#1#2% #1 name #2 value + {\setxvalue{#1}{\the\numexpr\csname#1\endcsname+#2\relax}} + +\def\decrementcounter#1#2% #1 name #2 value + {\setxvalue{#1}{\the\numexpr\csname#1\endcsname-#2\relax}} + +%D \macros +%D {savecounter,restorecounter} +%D +%D These two commands can be used to save and restore counter +%D values. Only one level is saved. + +\def\savecounter#1% + {{\scratchcounter\getvalue {#1}\setxvalue{!#1}{\the\scratchcounter}}} + +\def\restorecounter#1% + {{\scratchcounter\getvalue{!#1}\setxvalue {#1}{\the\scratchcounter}}} + +% == {\setxvalue{#1}{\getvalue{!#1}}} + +%D The next \ETEX\ based solution is some 15\% faster, which +%D goes unnoticed in any normal run, simply because these +%D macros are not used milions of times. + +\beginETEX \numexpr + +\def\makecounter#1% + {\global\@EA\let\csname#1\endcsname\zerocountervalue} % see earlier + +\def\countervalue#1% + {\ifcsname#1\endcsname\csname#1\endcsname\fi} + +\def\pluscounter#1% + {\@EA\xdef\csname#1\endcsname{\the\numexpr\csname#1\endcsname+\plusone\relax}} + +\def\minuscounter#1% + {\@EA\xdef\csname#1\endcsname{\the\numexpr\csname#1\endcsname-\plusone\relax}} + +\def\resetcounter#1% + {\global\@EA\let\csname#1\endcsname\zerocountervalue} + +\def\setcounter#1#2% + {\@EA\xdef\csname#1\endcsname{\the\numexpr#2\relax}} + +\def\savecounter#1% + {\@EA\xdef\csname !#1\endcsname{\the\numexpr\csname#1\endcsname\relax}} + +\def\restorecounter#1% + {\@EA\xdef\csname#1\endcsname{\the\numexpr\csname !#1\endcsname\relax}} + +\endETEX + +%D \macros +%D {beforesplitstring,aftersplitstring} +%D +%D These both commands split a string at a given point in two +%D parts, so \type{x.y} becomes \type{x} or \type{y}. +%D +%D \starttyping +%D \beforesplitstring test.tex\at.\to\filename +%D \aftersplitstring test.tex\at.\to\extension +%D \stoptyping +%D +%D The first routine looks (and is indeed) a bit simpler than +%D the second one. The alternative looking more or less like +%D the first one did not always give the results we needed. +%D Both implementations show some insight in the manipulation +%D of arguments. + +\def\beforesplitstring#1\at#2\to#3% + {\def\dosplitstring##1#2##2#2##3\\% + {\def#3{##1}}% + \@EA\dosplitstring#1#2#2\\} + +\def\aftersplitstring#1\at#2\to#3% + {\def\dosplitstring##1#2##2@@@##3\\% + {\def#3{##2}}% + \@EA\dosplitstring#1@@@#2@@@\\} + +%D \macros +%D {splitstring,greedysplitstring} +%D +%D A bonus macro. + +\def\splitstring#1\at#2\to#3\and#4% + {\def\dosplitstring##1#2##2\empty\empty\empty##3\\% + {\def#3{##1}% + \def\dosplitstring{##3}% + \ifx\dosplitstring\empty + \let#4\empty + \else + \def#4{##2}% + \fi}% + \@EA\dosplitstring#1\empty\empty\empty#2\empty\empty\empty\\} + +% Ok, but not for all cases: +% +% \def\greedysplitstring#1\at#2\to#3\and#4% +% {\edef\asciib{#1}% +% \let\asciic\asciib +% \let#3\empty +% \let#4\empty +% \doloop +% {\expandafter\splitstring\asciib\at#2\to\asciia\and\asciib +% \ifx\asciib\empty +% \exitloop +% \else +% \edef#3{\ifx#3\empty\else#3#2\fi\asciia}% +% \let#4\asciib +% \fi}% +% \ifx#3\empty\let#3\asciic\fi} +% +% The next one is some 25\% faster, but it hardly matters because +% we seldom use this macro. +% +% \def\greedysplitstring#1\at#2\to#3\and#4% +% {\edef\asciib{#1}% +% \let\asciic\asciib +% \let#3\empty +% \let#4\empty +% \def\dogreedysplitstring +% {\expandafter\splitstring\asciib\at#2\to\asciia\and\asciib +% \ifx\asciib\empty +% \expandafter\dogreedysplitstring +% \else +% \edef#3{\ifx#3\empty\else#3#2\fi\asciia}% +% \let#4\asciib +% \fi}% +% \dogreedysplitstring +% \ifx#3\empty\let#3\asciic\fi} +% +% The better alternative: + +\def\greedysplitstring#1\at#2\to#3\and#4% + {\edef\asciib{#1}% + \let\asciic\asciib + \let#3\empty + \let#4\empty + \doloop + {\expandafter\splitstring\asciib\at#2\to\asciia\and\asciib + \ifx\asciib\empty + \exitloop + \else + % not \edef#3{\ifx#3\empty\else#3#2\fi\asciia} else + % /root/path fails because then #3==empty + \edef#3{\ifcase\recurselevel\or\else#3#2\fi\asciia}% + \let#4\asciib + \fi}% + \ifx#3\empty\let#3\asciic\fi} + +%D \macros +%D {beforetestandsplitstring, +%D aftertestandsplitstring, +%D testandsplitstring} +%D +%D The next alternatives are for Simon Pepping. This time +%D the result is empty when no split is done. + +% \def\beforetestandsplitstring#1\at#2\to#3% +% {\def\dosplitstring##1#2##2#2##3\\{\doifelsenothing +% {##3}{\let#3\empty}{\def#3{##1}}}% +% \@EA\dosplitstring#1#2#2\\} +% +% \def\aftertestandsplitstring#1\at#2\to#3% +% {\def\dosplitstring ##1#2##2@@@##3\\{\doifelsenothing +% {##3}{\let#3\empty}{\def#3{##2}}}% +% \@EA\dosplitstring #1@@@#2@@@\\} +% +% \def\testandsplitstring#1\at#2\to#3\and#4% +% {\def\dosplitstring##1#2##2#2##3\\{\doifelsenothing +% {##3}{\let#3\empty\let#4\empty}{\def#3{##1}\def#4{##2}}}% +% \@EA\dosplitstring#1#2#2\\} +% +% faster: + +\def\beforetestandsplitstring#1\at#2\to#3% + {\def\dosplitstring##1#2##2#2##3##4\\% + {\ifx##3\empty\let#3\empty\else\def#3{##1}\fi}% + \@EA\dosplitstring#1#2#2\empty\\} + +\def\aftertestandsplitstring#1\at#2\to#3% + {\def\dosplitstring ##1#2##2@@@##3##4\\% + {\ifx##3\empty\let#3\empty\else\def#3{##2}\fi}% + \@EA\dosplitstring #1@@@#2@@@\empty\\} + +\def\testandsplitstring#1\at#2\to#3\and#4% + {\def\dosplitstring##1#2##2#2##3##4\\% + {\ifx##3\empty\let#3\empty\let#4\empty\else\def#3{##1}\def#4{##2}\fi}% + \@EA\dosplitstring#1#2#2\empty\\} + +%D \macros +%D {removesubstring} +%D +%D A first application of the two routines defined above is: +%D +%D \starttyping +%D \removesubstring-\from first-last\to\nothyphenated +%D \stoptyping +%D +%D Which in terms of \TEX\ looks like: + +%\def\removesubstring#1\from#2\to#3% +% {\doifinstringelse{#1}{#2} +% {\beforesplitstring#2\at#1\to\!!stringa +% \aftersplitstring #2\at#1\to\!!stringb +% \edef#3{\!!stringa\!!stringb}% +% \removesubstring#1\from#3\to#3} +% {}} + +\def\removesubstring#1\from#2\to#3% + {\splitstring#2\to\!!stringa\and\!!stringb + \dodoglobal#3{\!!stringa\!!stringb}} + +%D \macros +%D {appendtocommalist,prependtocommalist, +%D addtocommalist,removefromcommalist} +%D +%D When working with comma separated lists, one sooner or +%D later want the tools to append or remove items from such a +%D list. When we add an item, we first check if it's already +%D there. This means that every item in the list is unique. +%D +%D \starttyping +%D \addtocommalist {alfa} \name +%D \addtocommalist {beta} \name +%D \addtocommalist {gamma} \name +%D \removefromcommalist {beta} \name +%D \stoptyping +%D +%D These commands can be prefixed with \type{\doglobal}. The +%D implementation of the second command is more complecated, +%D because we have to take leading spaces into account. Keep in +%D mind that users may provide lists with spaces after the +%D commas. When one item is left, we also have to get rid of +%D trailing spaces. +%D +%D \starttyping +%D \def\words{alfa, beta, gamma, delta} +%D \def\words{alfa,beta,gamma,delta} +%D \stoptyping +%D +%D Removing an item takes more time than adding one. +%D +%D A fast appending alternative, without any testing, is +%D also provided: +%D +%D \starttyping +%D \appendtocommalist {something} \name +%D \prependtocommalist {something} \name +%D \stoptyping +%D +%D This can be implemented as follows: +%D +%D \starttyping +%D \def\appendtocommalist#1#2% +%D {\ifx#2\empty +%D \dodoglobal\edef#2{#1}% +%D \else % no test on empty +%D \dodoglobal\edef#2{#2,#1}% +%D \fi} +%D +%D \def\prependtocommalist#1#2% +%D {\ifx#2\empty +%D \dodoglobal\edef#2{#1}% +%D \else % no test on empty +%D \dodoglobal\edef#2{#1,#2}% +%D \fi} +%D \stoptyping +%D +%D The faster alternatives are: + +\def\appendtocommalist#1#2% + {\dodoglobal\edef#2{\ifx#2\empty\else#2,\fi#1}} + +\def\prependtocommalist#1#2% + {\dodoglobal\edef#2{#1\ifx#2\empty\else,#2\fi}} + +%D The old ones are: +%D +%D \starttyping +%D \def\addtocommalist#1#2% +%D {\ifx#2\empty +%D \dodoglobal\edef#2{#1}% +%D \else +%D \edef\!!stringa{#2,,}% +%D \beforesplitstring#2\at,,\to#2\relax +%D \ExpandBothAfter\doifinsetelse{#1}{#2} +%D {\resetglobal} +%D {\dodoglobal\edef#2{#2,#1}}% +%D \fi} +%D +%D \def\pretocommalist#1#2% +%D {\ifx#2\empty +%D \dodoglobal\edef#2{#1}% +%D \else +%D \edef\!!stringa{#2,,}% +%D \beforesplitstring#2\at,,\to#2\relax +%D \ExpandBothAfter\doifinsetelse{#1}{#2} +%D {\resetglobal} +%D {\dodoglobal\edef#2{#1,#2}}% +%D \fi} +%D +%D \def\doremovefromcommalist#1#2#3% nog \doglobal +%D {\edef\!!stringa{,,#3,,}% +%D \beforesplitstring\!!stringa\at,#1#2,\to\!!stringb +%D \aftersplitstring\!!stringa\at,#1#2,\to\!!stringc +%D \edef#3{\!!stringb,\!!stringc}% +%D \aftersplitstring#3\at,,\to#3\relax +%D \beforesplitstring#3\at,,\to#3} +%D +%D \def\removefromcommalist#1#2% +%D {\doremovefromcommalist{ }{#1}{#2}% +%D \doremovefromcommalist{}{#1}{#2}% +%D \dofrontstrip#2% +%D \dodoglobal\edef#2{#2}} +%D \stoptyping +%D +%D Significantly faster (especially for longer lists): + +\def\addtocommalist#1#2% {item} \cs + {\rawdoifinsetelse{#1}#2\resetglobal + {\dodoglobal\edef#2{\ifx#2\empty\else#2,\fi#1}}} + +\def\pretocommalist#1#2% {item} \cs + {\rawdoifinsetelse{#1}#2\resetglobal + {\dodoglobal\edef#2{#1\ifx#2\empty\else,#2\fi}}} + +\def\robustdoifinsetelse#1#2% + {\expanded{\convertargument#1}\to\!!stringa + \expanded{\convertargument#2}\to\!!stringb + \rawdoifinsetelse\!!stringa\!!stringb} + +\def\robustaddtocommalist#1#2% {item} \cs + {\robustdoifinsetelse{#1}#2\resetglobal + {\dodoglobal\edef#2{\ifx#2\empty\else#2,\fi#1}}} + +\def\robustpretocommalist#1#2% {item} \cs + {\robustdoifinsetelse{#1}#2\resetglobal + {\dodoglobal\edef#2{#1\ifx#2\empty\else,#2\fi}}} + +\def\xsplitstring#1#2% \cs {str} + {\def\dosplitstring##1,#2,##2,#2,##3\\% + {\edef\!!stringa{\bcleanedupcommalist##1\empty\empty\relax}% + \edef\!!stringb{\acleanedupcommalist##2,,\relax}}% + \@EA\dosplitstring\@EA,#1,,#2,,#2,\\} + +\def\bcleanedupcommalist#1#2#3\relax{\if#1,\else#1\fi\if#2,\else#2\fi#3} +\def\bcleanedupcommalist#1#2\relax{\if#1,\else#1\fi#2} +\def\acleanedupcommalist#1,,#2\relax{#1} + +% \def\removefromcommalist#1#2% +% {\expanded{\xsplitstring\noexpand#2{#1}}% +% \dodoglobal\edef#2% +% {\ifx\!!stringa\empty +% \!!stringb +% \else +% \@EA\acleanedupcommalist\!!stringa,,\relax +% \ifx\!!stringb\empty\else,\!!stringb\fi +% \fi}} + +\def\removefromcommalist#1#2% + {\rawdoifinsetelse{#1}#2% + {\expanded{\xsplitstring\noexpand#2{#1}}% + \dodoglobal\edef#2% + {\ifx\!!stringa\empty + \!!stringb + \else + \!!stringa\ifx\!!stringb\empty\else,\!!stringb\fi + \fi}} + \resetglobal} + +%D \macros +%D {substituteincommalist} +%D +%D Slow but seldom used, so for the moment we stick to this +%D implementation. +%D +%D \starttyping +%D \substituteincommalist{old}{new}{list} +%D \stoptyping + +\def\substituteincommalist#1#2#3% old, new, list (slooow) + {\edef\!!stringb{#1}% + \edef\!!stringd{#2}% + \let\!!stringa#3% + \let#3\empty + \def\dosubstituteincommalist##1% + {\edef\!!stringc{##1}% + \ifx\!!stringb\!!stringc + \ifx\!!stringd\empty\else + \edef#3{#3\ifx#3\empty\else,\fi\!!stringd}% + \fi + \def\docommand####1{\edef#3{#3,####1}}% + \else + \edef#3{#3\ifx#3\empty\else,\fi##1}% + \fi}% + \@EA\rawprocesscommacommand\@EA[\!!stringa]\dosubstituteincommalist} + +%D A not so useful macro: + +\def\dodofrontstrip[#1#2]#3% + {\ifx#1\space + \def#3{#2}% + \else + \def#3{#1#2}% + \fi} + +\def\dofrontstrip#1% + {\edef\!!stringa{#1}% + \ifx\!!stringa\empty \else + \@EA\dodofrontstrip\@EA[#1]#1% + \fi} + +%D \macros +%D {replaceincommalist} +%D +%D The next macro can be used to replace an indexed element +%D in a commalist: +%D +%D \starttyping +%D \replaceincommalist\MyList{2} +%D \stoptyping +%D +%D Element~2 will be replaced by the current meaning of the macro +%D \type {\newcommalistelement}. The old meaning is saved in +%D \type {\commalistelement}. The replacement honors grouped items, +%D like in: +%D +%D \starttyping +%D \def\MyList{a,b,c,d,e,f} \replaceincommalist\MyList{3} +%D \def\MyList{a,b,c,d,e,f} \replaceincommalist\MyList{3} +%D \def\MyList{a,{b,c},d,e,f} \replaceincommalist\MyList{3} +%D \def\MyList{a,b,c,{d,e,f}} \replaceincommalist\MyList{3} +%D \stoptyping + +\let\newcommalistelement\empty + +\def\replaceincommalist#1#2% #1 = commalistelement #2 = position starts at 1 + {\def\doreplaceincommalist##1% + {\ifnum\commalistcounter=#2\relax + \ifx\newcommalistelement\empty\else + \ifx\newcommalist\empty + \let\newcommalist\newcommalistelement + \else + \@EA\@EA\@EA\def\@EA\@EA\@EA\newcommalist\@EA\@EA\@EA + {\@EA\newcommalist\@EA,\newcommalistelement}% + \fi + \fi + \def\commalistelement{##1}% + \else + \ifx\newcommalist\empty + \ifx\nexttoken\bgroup % is known -) + \def\newcommalist{{##1}}% + \else + \def\newcommalist{##1}% + \fi + \else + \ifx\nexttoken\bgroup % is known -) + \@EA\def\@EA\newcommalist\@EA{\newcommalist,{##1}}% + \else + \@EA\def\@EA\newcommalist\@EA{\newcommalist,##1}% + \fi + \fi + \fi + \advance\commalistcounter\plusone}% + \let\commalistelement\empty + \let\newcommalist\empty + \commalistcounter\plusone + \@EA\processcommalist\@EA[#1]\doreplaceincommalist + \dodoglobal\let#1\newcommalist} + +%D \macros +%D {globalprocesscommalist} +%D +%D The commalist processing commands are characterized by the +%D fact that the way they handle expansion as well as the fact +%D that they can be nested. This makes them kind of useless for +%D handling comma lists in alignments. In these situations the +%D next macro can be of use. + +\def\globalprocesscommaitem#1,% + {\if]#1\else + \globalcommacommand{#1}% + \expandafter\globalprocesscommaitem + \fi} + +\def\globalprocesscommalist[#1]#2% + {\global\let\globalcommacommand#2% + \expandafter\globalprocesscommaitem#1,],} + +%D \macros +%D {startprocesscommalist,startprocesscommacommand} +%D +%D Two more: + +\long\def\startprocesscommalist[#1]#2\stopprocesscommalist + {\long\def\currentcommalistcommand##1{\def\currentcommalistitem{##1}#2}% + \processcommalist[#1]\currentcommalistcommand} + +\long\def\startprocesscommacommand[#1]#2\stopprocesscommacommand + {\long\def\currentcommalistcommand##1{\def\currentcommalistitem{##1}#2}% + \processcommacommand[#1]\currentcommalistcommand} + +%D \macros +%D {withoutpt,PtToCm, +%D numberofpoints,dimensiontocount} +%D +%D We can convert point into centimeters with: +%D +%D \starttyping +%D \PtToCm{dimension} +%D \stoptyping + +{\catcode`\.=\@@other + \catcode`\p=\@@other + \catcode`\t=\@@other + \gdef\WITHOUTPT#1pt{#1}} + +\def\withoutpt#1% + {\expandafter\WITHOUTPT#1} + +%D The capitals are needed because \type{p} and \type{t} have +%D \CATCODE~12, while macronames only permit tokens with the +%D \CATCODE~11. As a result we cannot use the \type{.group} +%D primitives. Those who want to know more about this kind of +%D manipulations, we advice to study the \TEX book in detail. +%D Because this macro does not do any assignment, we can use it +%D in the following way too. + +\def\PtToCm#1% + {\begingroup + \scratchdimen#1\relax + \scratchdimen0.0351459804\scratchdimen % 2.54/72.27 + \withoutpt\the\scratchdimen cm% + \endgroup} + +%D We also support: +%D +%D \starttyping +%D \numberofpoints {dimension} +%D \dimensiontocount {dimension} {\count} +%D \stoptyping +%D +%D Both macros return a rounded number. + +% \dimensiontocount{10.49pt}\scratchcounter \the\scratchcounter / \numberofpoints{10.49pt} +% \dimensiontocount{10.51pt}\scratchcounter \the\scratchcounter / \numberofpoints{10.51pt} + +\def\dimensiontocount#1#2{#2\numexpr\dimexpr#1\relax/\maxcard\relax} +\def\numberofpoints #1{\the\numexpr\dimexpr#1\relax/\maxcard\relax} + +%D \macros +%D {swapdimens,swapmacros} +%D +%D Simple but effective are the next two macros. There name +%D exactly states their purpose. The \type{\scratchdimen} and +%D \type{\!!stringa} can only be swapped when being the first +%D argument. + +\def\swapdimens#1#2% + {\scratchdimen #1\redoglobal #1#2\dodoglobal #2\scratchdimen} + +\def\swapmacros#1#2% + {\let\!!stringa#1\redoglobal\let#1#2\dodoglobal\let#2\!!stringa} + +%D \macros +%D {pushmacro,popmacro} +%D +%D Premature and a bit of beta, we offer: +%D +%D \starttyping +%D \pushmacro\macro +%D \popmacro\macro +%D \stoptyping +%D +%D Beware: global! + +% \def\@s@{@s@} +% +% \beginTEX +% +% \def\globalpushmacro#1% we can use a faster incement here +% {\@EA\doglobal\@EA\increment\csname\@s@*\string#1\endcsname +% \global\@EA\let\csname\csname\@s@*\string#1\endcsname*\string#1\endcsname#1} +% +% \def\globalpopmacro#1% \global\let +% {\global\@EA\let\@EA#1\csname\csname\@s@*\string#1\endcsname*\string#1\endcsname +% \@EA\doglobal\@EA\decrement\csname\@s@*\string#1\endcsname} +% +% \def\localpushmacro#1% this one can be used to push a value over an \egroup +% {\@EA\doglobal\@EA\increment\csname\@s@**\string#1\endcsname +% \global\@EA\let\csname\csname\@s@**\string#1\endcsname**\string#1\endcsname#1} +% +% \def\localpopmacro#1% \local\let +% {\@EA\let\@EA#1\csname\csname\@s@**\string#1\endcsname**\string#1\endcsname +% \global\@EA\decrement\csname\@s@**\string#1\endcsname} +% +% \endTEX +% +% \beginETEX \newcount +% +% \def\globalpushmacro#1% +% {\ifcsname\@s@*\string#1\endcsname \else +% \@EA\newcount\csname\@s@*\string#1\endcsname +% \fi +% \global\advance\csname\@s@*\string#1\endcsname \plusone +% \global\@EA\let\csname\the\csname\@s@*\string#1\endcsname*\string#1\endcsname#1} +% +% \def\globalpopmacro#1% \global\let +% {\global\@EA\let\@EA#1\csname\the\csname\@s@*\string#1\endcsname*\string#1\endcsname +% \global\advance\csname\@s@*\string#1\endcsname \minusone} +% +% \def\localpushmacro#1% this one can be used to push a value over an \egroup +% {\ifcsname\@s@**\string#1\endcsname \else +% \@EA\newcount\csname\@s@**\string#1\endcsname +% \fi +% \global\advance\csname\@s@**\string#1\endcsname \plusone +% \global\@EA\let\csname\the\csname\@s@**\string#1\endcsname**\string#1\endcsname#1} +% +% \def\localpopmacro#1% \local\let +% {\@EA\let\@EA#1\csname\the\csname\@s@**\string#1\endcsname**\string#1\endcsname +% \global\advance\csname\@s@**\string#1\endcsname \minusone } +% +% \endETEX +% +% some 5% faster (used a lot in typescripts, so ...) + +\def\@sl@{@sl@} +\def\@sg@{@sg@} + +\let\@@pushedmacro\empty + +\beginTEX + +\def\globalpushmacro#1% we can use a faster incement here + {\xdef\@@pushedmacro{\string#1}% + \@EA\doglobal\@EA\increment\csname\@sg@\@@pushedmacro\endcsname + \global\@EA\let\csname\csname\@sg@\@@pushedmacro\endcsname\@@pushedmacro\endcsname#1} + +\def\globalpopmacro#1% + {\xdef\@@pushedmacro{\string#1}% + \global\@EA\let\@EA#1\csname\csname\@sg@\@@pushedmacro\endcsname\@@pushedmacro\endcsname + \@EA\doglobal\@EA\decrement\csname\@sg@\@@pushedmacro\endcsname} + +\def\localpushmacro#1% this one can be used to push a value over an \egroup + {\xdef\@@pushedmacro{\string#1}% + \@EA\doglobal\@EA\increment\csname\@sl@\@@pushedmacro\endcsname + \global\@EA\let\csname\csname\@sl@\@@pushedmacro\endcsname\@@pushedmacro\endcsname#1} + +\def\localpopmacro#1% + {\xdef\@@pushedmacro{\string#1}% + \@EA\let\@EA#1\csname\csname\@sl@\@@pushedmacro\endcsname\@@pushedmacro\endcsname + \global\@EA\decrement\csname\@sl@\@@pushedmacro\endcsname} + +\endTEX + +\beginETEX + +\def\globalpushmacro#1% + {\xdef\@@pushedmacro{\string#1}% + \ifcsname\@sg@\@@pushedmacro\endcsname \else + \@EA\newcount\csname\@sg@\@@pushedmacro\endcsname + \fi + \global\advance\csname\@sg@\@@pushedmacro\endcsname \plusone + \global\@EA\let\csname\the\csname\@sg@\@@pushedmacro\endcsname\@@pushedmacro\endcsname#1} + +\def\globalpopmacro#1% + {\xdef\@@pushedmacro{\string#1}% + \global\@EA\let\@EA#1\csname\the\csname\@sg@\@@pushedmacro\endcsname\@@pushedmacro\endcsname + \global\advance\csname\@sg@\@@pushedmacro\endcsname \minusone} + +\def\localpushmacro#1% this one can be used to push a value over an \egroup + {\xdef\@@pushedmacro{\string#1}% + \ifcsname\@sl@\@@pushedmacro\endcsname \else + \@EA\newcount\csname\@sl@\@@pushedmacro\endcsname + \fi + \global\advance\csname\@sl@\@@pushedmacro\endcsname \plusone + \global\@EA\let\csname\the\csname\@sl@\@@pushedmacro\endcsname\@@pushedmacro\endcsname#1} + +\def\localpopmacro#1% + {\xdef\@@pushedmacro{\string#1}% + \@EA\let\@EA#1\csname\the\csname\@sl@\@@pushedmacro\endcsname\@@pushedmacro\endcsname + \global\advance\csname\@sl@\@@pushedmacro\endcsname \minusone } + +\endETEX + +% \let\pushmacro\globalpushmacro +% \let\popmacro \globalpopmacro + +\let\pushmacro\localpushmacro +\let\popmacro \localpopmacro + +%D \macros +%D {setlocalhsize} +%D +%D Sometimes we need to work with the \type{\hsize} that is +%D corrected for indentation and left and right skips. The +%D corrected value is available in \type{\localhsize}, which +%D needs to be calculated with \type{\setlocalhsize} first. +%D +%D \starttyping +%D \setlocalhsize \hbox to \localhsize{...} +%D \setlocalhsize[-1em] \hbox to \localhsize{...} +%D \setlocalhsize[.5ex] \hbox to \localhsize{...} +%D \stoptyping +%D +%D These examples show us that an optional can be used. The +%D value provided is added to \type{\localhsize}. + +\newdimen\localhsize + +\def\complexsetlocalhsize[#1]% don't change ! + {\localhsize\hsize + \ifnum\hangafter<\zerocount + \advance\localhsize\ifdim\hangindent>\zeropoint-\fi\hangindent + \fi + \advance\localhsize -\leftskip + \advance\localhsize -\rightskip + \advance\localhsize #1\relax} + +\def\simplesetlocalhsize + {\complexsetlocalhsize[\zeropoint]} + +\definecomplexorsimple\setlocalhsize + +%D \macros +%D {doifvalue,doifnotvalue,doifelsevalue, +%D doifnothing,doifsomething,doifelsenothing, +%D doifvaluenothing,doifvaluesomething,doifelsevaluenothing} +%D +%D These long named \type{\if} commands can be used to access +%D macros (or variables) that are normally accessed by using +%D \type{\getvalue}. Using these alternatives safes us three +%D tokens per call. Anyone familiar with the not||values +%D ones, can derive their meaning from the definitions. + + \def\doifvalue#1{\doif {\csname#1\endcsname}} + \def\doifnotvalue#1{\doifnot {\csname#1\endcsname}} + \def\doifelsevalue#1{\doifelse{\csname#1\endcsname}} + + \def\doifnothing#1{\doif {#1}{}} + \def\doifsomething#1{\doifnot {#1}{}} + \def\doifelsenothing#1{\doifelse{#1}{}} + + \def\doifvaluenothing#1{\doif {\csname#1\endcsname}{}} + \def\doifvaluesomething#1{\doifnot {\csname#1\endcsname}{}} +\def\doifelsevaluenothing#1{\doifelse{\csname#1\endcsname}{}} + +%D Faster but spoiling inheritance (copying parameters): +%D +%D \starttyping +%D \def\doifelsevaluesomething#1#2#3% +%D {\expandafter\ifx\csname#1\endcsname\empty#3\else#2\fi} +%D +%D \def\doifvaluesomething#1#2% +%D {\expandafter\ifx\csname#1\endcsname\empty\else#2\fi} +%D +%D \def\doifvaluenothing#1#2% +%D {\expandafter\ifx\csname#1\endcsname\empty#2\fi} +%D \stoptyping +%D +%D Slightly more efficient: + + \def\doifnothing{\doif \empty} + \def\doifsomething{\doifnot \empty} +\def\doifelsenothing{\doifelse\empty} + +%D The somewhat faster alternatives are: + +\long\def\doifvalue#1#2% + {\edef\!!stringa{\csname#1\endcsname}\edef\!!stringb{#2}% + \ifx\!!stringa\!!stringb + \expandafter\firstofoneargument + \else + \expandafter\gobbleoneargument + \fi} + +\long\def\doifnotvalue#1#2% + {\edef\!!stringa{\csname#1\endcsname}\edef\!!stringb{#2}% + \ifx\!!stringa\!!stringb + \expandafter\gobbleoneargument + \else + \expandafter\firstofoneargument + \fi} + +\long\def\doifelsevalue#1#2% + {\edef\!!stringa{\csname#1\endcsname}\edef\!!stringb{#2}% + \ifx\!!stringa\!!stringb + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +\long\def\doifnothing#1% + {\edef\!!stringa{#1}% + \ifx\!!stringa\empty + \expandafter\firstofoneargument + \else + \expandafter\gobbleoneargument + \fi} + +\long\def\doifsomething#1% + {\edef\!!stringa{#1}% + \ifx\!!stringa\empty + \expandafter\gobbleoneargument + \else + \expandafter\firstofoneargument + \fi} + +\long\def\doifelsenothing#1% + {\edef\!!stringa{#1}% + \ifx\!!stringa\empty + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +\long\def\doifsomethingelse#1% + {\edef\!!stringa{#1}% + \ifx\!!stringa\empty + \expandafter\secondoftwoarguments + \else + \expandafter\firstoftwoarguments + \fi} + +\long\def\doifvaluenothing#1% + {\edef\!!stringa{\csname#1\endcsname}% + \ifx\!!stringa\empty + \expandafter\firstofoneargument + \else + \expandafter\gobbleoneargument + \fi} + +\long\def\doifvaluesomething#1% + {\edef\!!stringa{\csname#1\endcsname}% + \ifx\!!stringa\empty + \expandafter\gobbleoneargument + \else + \expandafter\firstofoneargument + \fi} + +\long\def\doifelsevaluenothing#1% + {\edef\!!stringa{\csname#1\endcsname}% + \ifx\!!stringa\empty + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +%D \macros +%D {doifemptyelsevalue, doifemptyvalue, doifnotemptyvalue} +%D +%D Also handy: + +\def\doifemptyelsevalue#1% + {\@EA\ifx\csname#1\endcsname\empty + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +\def\doifemptyvalue#1% + {\@EA\ifx\csname#1\endcsname\empty + \expandafter\firstofoneargument + \else + \expandafter\gobbleoneargument + \fi} + +\def\doifnotemptyvalue#1% + {\@EA\ifx\csname#1\endcsname\empty + \expandafter\gobbleoneargument + \else + \expandafter\firstofoneargument + \fi} + +%D \macros +%D {doifallcommonelse} +%D +%D A complete match of two sets can be tested with +%D \type {\doifallcommonelse}, where the first two +%D arguments are sets. + +%\def\doifallcommonelse#1#2#3#4% +% {\def\p!docommoncheck##1% +% {\doifnotinset{##1}{#2}{\donefalse}% +% \ifdone\else\quitcommalist\fi}% +% \donetrue +% \processcommalist[#1]\p!docommoncheck +% \ifdone#3\else#4\fi} + +\def\@@doifallcommonelse#1#2#3#4% slow + {\def\p!docommoncheck##1% + {\doifnotinset{##1}{#4}\donefalse + \ifdone\else\expandafter\quitcommalist\fi}% + \donetrue + \processcommalist[#3]\p!docommoncheck + \ifdone\expandafter#1\else\expandafter#2\fi} + +\def\doifallcommonelse + {\@@doifallcommonelse\firstoftwoarguments\secondoftwoarguments} + +\def\doifallcommon + {\@@doifallcommonelse\firstofonearguments\gobbleoneargument} + +\def\doifnotallcommon + {\@@doifallcommonelse\gobbleoneargument\firstofonearguments} + +%D \macros +%D {DOIF,DOIFELSE,DOIFNOT} +%D +%D \TEX\ is case sensitive. When comparing arguments, this +%D feature sometimes is less desirable, for instance when we +%D compare filenames. The next three alternatives upcase their +%D arguments before comparing them. +%D +%D \starttyping +%D \DOIF {string1} {string2} {...} +%D \DOIFNOT {string1} {string2} {...} +%D \DOIFELSE {string1} {string2} {then ...}{else ...} +%D \stoptyping +%D +%D We have to use a two||step implementation, because the +%D expansion has to take place outside \type{\uppercase}. + +\def\p!DOIF#1#2% + {\uppercase{\ifinstringelse{$#1$}{$#2$}}% + \expandafter\firstofoneargument + \else + \expandafter\gobbleoneargument + \fi} + +\def\p!DOIFNOT#1#2% + {\uppercase{\ifinstringelse{$#1$}{$#2$}}% + \expandafter\gobbleoneargument + \else + \expandafter\firstofoneargument + \fi} + +\def\p!DOIFELSE#1#2% + {\uppercase{\ifinstringelse{$#1$}{$#2$}}% + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +\def\p!DOIFINSTRINGELSE#1#2% + {\uppercase{\ifinstringelse{#1}{#2}}% + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +\def\DOIF {\ExpandBothAfter\p!DOIF} +\def\DOIFNOT {\ExpandBothAfter\p!DOIFNOT} +\def\DOIFELSE {\ExpandBothAfter\p!DOIFELSE} +\def\DOIFINSTRINGELSE {\ExpandBothAfter\p!DOIFINSTRINGELSE} + +%D \macros +%D {dosingleargumentwithset, +%D dodoubleargumentwithset,dodoubleemptywithset, +%D dotripleargumentwithset,dotripleemptywithset} +%D +%D These maybe too mysterious macros enable us to handle more +%D than one setup at once. +%D +%D \starttyping +%D \dosingleargumentwithset \command[#1] +%D \dodoubleargumentwithset \command[#1][#2] +%D \dotripleargumentwithset \command[#1][#2][#3] +%D \dodoubleemptywithset \command[#1][#2] +%D \dotripleemptywithset \command[#1][#2][#3] +%D \stoptyping +%D +%D The first macro calls \type{\command[##1]} for each string +%D in the set~\type{#1}. The second one calls for +%D \type{\commando[##1][#2]} and the third, well one may guess. +%D These commands support constructions like: +%D +%D \starttyping +%D \def\dodefinesomething[#1][#2]% +%D {\getparameters[\??xx#1][#2]} +%D +%D \def\definesomething% +%D {\dodoubleargumentwithset\dodefinesomething} +%D \stoptyping +%D +%D Which accepts calls like: +%D +%D \starttyping +%D \definesomething[alfa,beta,...][variable=...,...] +%D \stoptyping +%D +%D Now a whole bunch of variables like \type{\@@xxalfavariable} +%D and \type{\@@xxbetavariable} is defined. + +\def\dodoublewithset#1#2% + {\def\dododoublewithset[##1][##2]% + {\doifsomething{##1} + {\def\dodododoublewithset####1{#2[####1][##2]}% + \processcommalist[##1]\dodododoublewithset}}% + #1\dododoublewithset} + +\def\dodoubleemptywithset {\dodoublewithset\dodoubleempty} +\def\dodoubleargumentwithset{\dodoublewithset\dodoubleargument} + +\def\dotriplewithset#1#2% + {\def\dodotriplewithset[##1][##2][##3]% + {\doifsomething{##1} + {\def\dododotriplewithset####1{#2[####1][##2][##3]}% + \processcommalist[##1]\dododotriplewithset}}% + #1\dodotriplewithset} + +\def\dotripleemptywithset {\dotriplewithset\dotripleempty} +\def\dotripleargumentwithset{\dotriplewithset\dotripleargument} + +%D \macros +%D {stripcharacters,stripspaces} +%D +%D The next command was needed first when we implemented +%D the \CONTEXT\ interactivity macros. When we use labeled +%D destinations, we often cannot use all the characters we +%D want. We therefore strip some of the troublemakers, like +%D spaces, from the labels before we write them to the +%D \DVI||file, which passes them to for instance a PostScript +%D file. +%D +%D \starttyping +%D \stripspaces\from\one\to\two +%D \stoptyping +%D +%D Both the old string \type{\one} and the new one \type{\two} +%D are expanded. This command is a special case of: +%D +%D \starttyping +%D \stripcharacter\char\from\one\to\two +%D \stoptyping +%D +%D As we can see below, spaces following a control sequence are +%D to enclosed in \type{{}}. + +% keep this one: +% +% \def\stripcharacter#1\from#2\to#3% +% {\def\dostripcharacter##1#1##2\end +% {\edef\!!strippedstring{\!!strippedstring##1}% +% \doifnotempty{##2}{\dostripcharacter##2\end}}% +% \let\!!strippedstring\empty +% \edef\!!stringa{#2}% +% \@EA\dostripcharacter\!!stringa#1\end +% \dodoglobal\let#3\!!strippedstring} +% +% the following is better (comes from syst-loc): + +\def\stripcharacter#1\from#2\to#3% + {\def\dostripcharacter##1#1##2\end + {\edef\!!strippedstring{\!!strippedstring##1}% + \doifnotempty{##2}{\dostripcharacter##2\end}}% + \let\!!strippedstring\empty + \edef\!!stringa{#2}% + \@EA\dostripcharacter\!!stringa#1\end + \dodoglobal\let#3\!!strippedstring} + +\def\stripspaces\from#1\to#2% will become \unspacestring#1\from#2 + {\stripcharacter{ }\from#1\to#2} + +%D \macros +%D {unspacestring} +%D +%D The next macro does the same but is more compatible with other macros, +%D like \type {\convert...}. + +\def\unspacestring#1\to#2% + {\stripcharacter{ }\from#1\to#2} + +%D \macros +%D {executeifdefined} +%D +%D \CONTEXT\ uses one auxiliary file for all data concerning +%D tables of contents, references, two||pass optimizations, +%D sorted lists etc. This file is loaded as many times as +%D needed. During such a pass we skip the commands thate are of +%D no use at that moment. Because we don't want to come into +%D trouble with undefined auxiliary commands, we call the +%D macros in a way similar to \type{\getvalue}. The next macro +%D take care of such executions and when not defined, gobbles +%D the unwanted arguments. +%D +%D \starttyping +%D \executeifdefined{name}\gobbleoneargument +%D \stoptyping +%D +%D We can of course gobble more arguments using the +%D appropriate gobbling command. + +\newif\ifexecuted % general purpose + +\def\executeifdefined#1#2% + {\ifundefined{#1}% + \def\next{#2}% + \else + \def\next{\getvalue{#1}}% + \fi + \next} + +%D Just for fun I times the next alternative: it was roughly +%D timed about 15\% faster than the default (10+ sec to 9 sec)! + +\beginTEX + +\def\executeifdefined#1% #2 / never change this one again + {\ifundefined{#1}% + \expandafter\secondoftwoarguments + \else + \expandafter\firstoftwoarguments + \fi + {\csname#1\endcsname}} + +\endTEX + +\beginETEX + +% \def\executeifdefined#1% #2 / never change this one again +% {\ifcsname#1\endcsname +% \expandafter\firstoftwoarguments +% \else +% \expandafter\secondoftwoarguments +% \fi +% {\csname#1\endcsname}} + +\def\executeifdefined#1% #2 / never change this one again + {\ifcsname#1\endcsname + \csname#1\expandafter\expandafter\expandafter\endcsname\expandafter\gobbleoneargument + \else + \expandafter\firstofoneargument + \fi} + +\endETEX + +% \letvalue{f }\firstofoneargument \def\executeifdefined#1{\csname\ifcsname#1\endcsname#1\else f \fi\endcsname} + +%D This one also has the advantage that it is fully +%D expandable and that it can be used after an assignment. + +%D \macros +%D {executeifdefinedcs} +%D +%D An also fully expandable variant is the following: +%D +%D \starttyping +%D \executeifdefinedcs{a}{b} +%D \stoptyping +%D +%D In dit geval zijn beide argumenten csnames. + +\def\executeifdefinedcs#1#2% + {\csname\ifundefined{#1}#2\else#1\fi\endcsname} + +%D We considered an alternative implementation accepting +%D commands directly, like: +%D +%D \starttyping +%D \executeifdefined\name\gobblefivearguments +%D \stoptyping +%D +%D For the moment we don't need this one, so we stick to the +%D faster one. + +%D \macros +%D {executeandforget} +%D +%D The following macros were requested by Simon. Watch the +%D global variant. +%D +%D \starttyping +%D \executeandforget\SomeCommand +%D \doglobal\executeandforget\AnotherCommand +%D \stoptyping + +\def\executeandforget#1% + {\global\let\@@expanded#1% + \dodoglobal\let#1\relax + \@@expanded} + +%D \macros +%D {doifsomespaceelse} +%D +%D The next command checks a string on the presence of a space +%D and executed a command accordingly. +%D +%D \starttyping +%D \doifsomespaceelse {tekst} {then ...} {else ...} +%D \stoptyping +%D +%D We use this command in \CONTEXT\ for determing if an +%D argument must be broken into words when made interactive. +%D Watch the use of \type{\noexpand}. + +%D Is this one still needed? + +% \long\def\doifsomespaceelse#1#2#3% +% {\def\p!doifsomespaceelse##1 ##2##3\war% +% {\if\noexpand##2@#3\else#2\fi}% +% \p!doifsomespaceelse#1 @ @\war} + +\def\p!doifsomespaceelse#1 #2#3\war{\if\noexpand#2@} + +\long\def\doifsomespaceelse#1% % #2#3% + {\p!doifsomespaceelse#1 @ @\war % #3\else#2\fi} + \expandafter\secondoftwoarguments + \else + \expandafter\firstoftwoarguments + \fi} + +%D \macros +%D {adaptdimension,balancedimensions} +%D +%D Again we introduce some macros that are closely related to +%D an interface aspect of \CONTEXT. The first command can be +%D used to adapt a \DIMENSION. +%D +%D \starttyping +%D \adaptdimension {dimension} {value} +%D \stoptyping +%D +%D When the value is preceed by a \type{+} or minus, the +%D dimension is advanced accordingly, otherwise it gets the +%D value. + +% \def\doadaptdimension#1#2\\#3\\% +% {\if#1+% +% \dodoglobal\advance#3 #1#2\relax +% \else\if#1-% +% \dodoglobal\advance#3 #1#2\relax +% \else +% \dodoglobal#3=#1#2\relax +% \fi\fi} +% +% more fuzzy but also more efficient + +\def\doadaptdimension#1#2\\#3\\% + {\if#1+% + \dodoglobal\advance + \else\if#1-% + \dodoglobal\advance + \else + \dodoglobal + \fi\fi + #3 #1#2\relax} + +\def\adaptdimension#1#2% + {\expandafter\doadaptdimension#2\\#1\\} + +%D A second command takes two \DIMENSIONS. Both are adapted, +%D depending on the sign of the given value. +%D maat. This time we take the value as it is, and don't look +%D explicitly at the preceding sign. +%D +%D \starttyping +%D \balancedimensions {dimension 1} {dimension 2} {value} +%D \stoptyping +%D +%D When a positive value is given, the first dimension is +%D incremented, the second ond is decremented. A negative value +%D has the opposite result. + +\def\balancedimensions#1#2#3% + {\scratchdimen#3\relax + \redoglobal\advance#1 \scratchdimen + \dodoglobal\advance#2 -\scratchdimen} + +%D Both commands can be preceded by \type{\doglobal}. Here we +%D use \type{\redo} first, because \type{\dodo} resets the +%D global character. + +%D \macros +%D {processseparatedlist} +%D +%D Maybe a bit late, but here is a more general version of the +%D \type{\processcommalist} command. This time we don't handle +%D nesting but accept arbitrary seperators. +%D +%D \starttyping +%D \processseparatedlist[list][separator]\command +%D \stoptyping +%D +%D One can think of things like: +%D +%D \starttyping +%D \processseparatedlist[alfa+beta+gamma][+]\message +%D \stoptyping + +%D First we show the simple alternative: +%D +%D \starttyping +%D \def\processseparatedlist[#1][#2]#3% +%D {\def\doprocessseparatedlist##1##2#2% +%D {\if]##1% +%D \let\next=\relax +%D \else\if]##2% +%D \let\next=\relax +%D \else\ifx\blankspace##2% +%D #3{##1}% +%D \let\next=\doprocessseparatedlist +%D \else +%D #3{##1##2}% +%D \let\next=\doprocessseparatedlist +%D \fi\fi\fi +%D \next}% +%D \doprocessseparatedlist#1#2]#2} +%D \stoptyping +%D +%D However, we want to handle all situations, like: +%D +%D \startbuffer +%D \processseparatedlist[{aap noot}] [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii} +%D \processseparatedlist[{aap} {noot}][ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii} +%D \processseparatedlist[aap {noot}] [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii} +%D \processseparatedlist[aap noot] [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii} +%D \stopbuffer +%D +%D \typebuffer \getbuffer +%D +%D Therefore we smuggle a \type {\relax} in front of the +%D argument, which we remove afterwards. + +% \def\doprocessseparatedlist#1]#2[#3]#4% +% {\def\dodoprocessseparatedlist##1##2#3% +% {\if]##1% +% \let\dodoprocessseparatedlist\relax +% \else\if]##2% +% \let\dodoprocessseparatedlist\relax +% \else\ifx\blankspace##2% +% #4{##1}% +% \else +% #4{##1##2}% +% \fi\fi\fi +% \dodoprocessseparatedlist}% +% \@EA\dodoprocessseparatedlist\gobbleoneargument#1#3]#3} + +% testcase Vit Zyka: +% +% \def\Do#1{(#1)} +% 1\processseparatedlist[{aap noot}] [ ]\Do \par +% 2\processseparatedlist[{aap} {noot}][ ]\Do \par +% 3\processseparatedlist[aap {noot}] [ ]\Do \par +% 4\processseparatedlist[aap noot] [ ]\Do \par +% 5\processseparatedlist[aap;noot;a noot;noot a; noot a noot][;]\Do \par +% 6\processseparatedlist[][;]\Do \par +% 7\processseparatedlist[;][;]\Do \par + +\def\doprocessseparatedlist#1]#2[#3]#4% + {\def\dodoprocessseparatedlist##1##2#3% + {\def\!!stringa{##2}% suggested by VZ + \if]##1% + \let\dodoprocessseparatedlist\relax + \else\ifx\blankspace\!!stringa + #4{##1}% + \else\if]##2% + \let\dodoprocessseparatedlist\relax + \else + #4{##1##2}% + \fi\fi\fi + \dodoprocessseparatedlist}% + \@EA\dodoprocessseparatedlist\gobbleoneargument#1#3]#3} + +\def\processseparatedlist[% + {\doprocessseparatedlist\relax} + +%D \macros +%D {processlist} +%D +%D An even more general list processing macro is the +%D following one: +%D +%D \starttyping +%D \processlist{beginsym}{endsym}{separator}\docommand list +%D \stoptyping +%D +%D This one supports arbitrary open and close symbols as well +%D as user defined separators. +%D +%D \starttyping +%D \processlist(){=>}\docommand(a=>b=>c=>d) +%D \stoptyping + +\long\def\processlist#1#2#3#4% no blank skipping ! + {\def\doprocesslist##1#2% + {\def\dodoprocesslist####1####2#3% + {\ifx#2####1% + \let\dodoprocesslist\relax + \else\ifx#2####2% + \let\dodoprocesslist\relax + \else + #4{####1####2}% + \fi\fi + \dodoprocesslist}% + \expandafter\dodoprocesslist\gobbleoneargument##1#3#2#3}% + \def\dodoprocesslist#1% + {\doprocesslist\relax}% + \dodoprocesslist} + +% %D \macros +% %D {dohonorgroupedargument} +% %D +% %D The previous macro uses yet another auxiliary macro to +% %D handle the special case. +% +% \def\dohonorgroupedargument#1[% +% {\doifnextbgroupelse{\dodohonorgroupedargument#1}{#1[}} +% +% \def\dodohonorgroupedargument#1#2% +% {#1[{{#2}}} + +%D \macros +%D {processassignlist} +%D +%D Is possible to combine an assignment list with one +%D containing keywords. Assignments are treated accordingly, +%D keywords are treated by \type{\command}. +%D +%D \starttyping +%D \processassignlist[...=...,...=...,...]\commando +%D \stoptyping +%D +%D This command can be integrated in \type{\getparameters}, but +%D we decided best not to do so. + +\def\processassignlist#1[#2]#3% + {\def\p!dodogetparameter[##1=##2=##3]% + {\doifnot{##3}\relax{#3{##1}}}% + \def\p!dogetparameter##1% + {\p!dodogetparameter[##1==\relax]}% + \processcommalist[#2]\p!dogetparameter} + +% too ugly +% +% %D \macros +% %D {DoAfterFi,DoAfterFiFi} +% %D +% %D Sometimes \type{\fi}'s can get into the way. We can reach +% %D over such a troublemaker with: +% %D +% %D \starttyping +% %D \DoAfterFi{some commands} +% %D \DoAfterFiFi{some commands} +% %D \stoptyping +% %D +% %D It saves us a \type{\next} construction. Skipping +% %D \type{\else...\fi} is more tricky, so this one is not +% %D provided. +% +% \def\DoAfterFi#1\fi{\fi#1} +% \def\DoAfterFiFi#1\fi#2\fi{\fi\fi#1} + +%D \macros +%D {untextargument +%D untexcommand} +%D +%D When manipulating data(bases) and for instance generating +%D index entries, the next three macros can be of help: +%D +%D \starttyping +%D \untextargument{...}\to\name +%D \untexcommand {...}\to\name +%D \stoptyping +%D +%D They remove braces and backslashes and give us something to +%D sort. + +\def\untexsomething + {\begingroup + \catcode`\{=\@@ignore + \catcode`\}=\@@ignore + \escapechar\minusone + \dountexsomething} + +\long\def\dountexsomething#1#2\to#3% + {\doglobal#1#2\to\untexedargument + \endgroup + \let#3\untexedargument} + +\def\untexargument{\untexsomething\convertargument} +\def\untexcommand {\untexsomething\convertcommand} + +%D \macros +%D {ScaledPointsToBigPoints,ScaledPointsToWholeBigPoints} +%D +%D One characteristic of \POSTSCRIPT\ and \PDF\ is that both +%D used big points (\TEX's bp). The next macros convert points +%D and scaled points into big points. +%D +%D \starttyping +%D \ScaledPointsToBigPoints {number} \target +%D \ScaledPointsToWholeBigPoints {number} \target +%D \stoptyping +%D +%D The magic factor $72/72.27$ can be found in most \TEX\ +%D related books. + +% \def\ScaledPointsToBigPoints #1{\PointsToBigPoints {#1sp}} +% \def\ScaledPointsToWholeBigPoints#1{\PointsToWholeBigPoints{#1sp}} +% +% \def\PointsToBigPoints#1#2% +% {\scratchdimen#1% +% \scratchdimen.996264\scratchdimen +% \edef#2{\withoutpt\the\scratchdimen}} +% +% \def\PointsToWholeBigPoints#1#2% +% {\scratchdimen#1% +% \scratchdimen.996264\scratchdimen +% \scratchcounter\scratchdimen +% \advance\scratchcounter \medcard +% \divide\scratchcounter \maxcard +% \edef#2{\the\scratchcounter}} + +% \PointsToBigPoints{10.53940pt}\test \test +% \PointsToBigPoints{10.53941pt}\test \test +% \PointsToBigPoints{10.53942pt}\test \test + +% \PointsToWholeBigPoints{10.53940pt}\test \test +% \PointsToWholeBigPoints{10.53941pt}\test \test +% \PointsToWholeBigPoints{10.53942pt}\test \test + +\beginTEX + + \def\PointsToBigPoints#1#2% + {\scratchdimen#1% + \scratchdimen.996264\scratchdimen + \edef#2{\withoutpt\the\scratchdimen}} + + \def\PointsToWholeBigPoints#1#2% + {\scratchdimen#1% + \scratchdimen.996264\scratchdimen + \scratchcounter\scratchdimen + \advance\scratchcounter \medcard + \divide\scratchcounter \maxcard + \edef#2{\the\scratchcounter}} + +\endTEX + +\beginETEX \dimexpr \numexpr + +% \def\PointsToBigPoints#1#2% +% {\edef#2{\withoutpt\the\dimexpr(.996264\dimexpr(#1))}} + +% \def\PointsToWholeBigPoints#1#2% +% {\edef#2{\the\numexpr(\numexpr(\dimexpr(.996264\dimexpr(#1)))/\maxcard)}} + + \def\PointsToBigPoints#1#2% + {\edef#2{\withoutpt\the\dimexpr.996264\dimexpr#1\relax\relax}} + + \def\PointsToWholeBigPoints#1#2% + {\edef#2{\the\numexpr\dimexpr.996264\dimexpr#1\relax\relax/\maxcard\relax}} + +\endETEX + +\def\ScaledPointsToBigPoints #1{\PointsToBigPoints {\number#1\scaledpoint}} +\def\ScaledPointsToWholeBigPoints#1{\PointsToWholeBigPoints{\number#1\scaledpoint}} + +%D \macros +%D {PointsToReal} +%D +%D Points can be stripped from their suffix by using +%D \type{\withoutpt}. The next macro enveloppes this macro. +%D +%D \starttyping +%D \PointsToReal {dimension} \target +%D \stoptyping + +\def\PointsToReal#1#2% + {\scratchdimen#1% + \edef#2{\withoutpt\the\scratchdimen}} + +%D \macros +%D {dontleavehmode} +%D +%D Sometimes when we enter a paragraph with some command, the +%D first token gets the whole first line. We can prevent this +%D by saying: +%D +%D \starttyping +%D \dontleavehmode +%D \stoptyping +%D +%D This command is used in for instance the language module +%D \type{lang-ini}. The first version was: +%D +%D \starttyping +%D \def\dontleavehmode{\ifhmode\else\ifmmode\else$ $\fi\fi} +%D \stoptyping +%D +%D Next, Taco came with a better alternative (using mathsurround): +%D +%D \starttyping +%D \def\dontleavehmode +%D {\ifhmode\else \ifmmode\else +%D {\mathsurround\zeropoint\everymath\emptytoks$ $}% +%D \fi \fi} +%D \stoptyping +%D +%D And finaly we got the following alternative, one that avoids +%D interfering grouping at the cost of a box. + +\newbox\@@dlhbox + +\unexpanded \def\dontleavehmode + {\ifhmode\else \ifmmode\else + \setbox\@@dlhbox\hbox{\mathsurround\zeropoint\everymath\emptytoks$ $}\unhbox\@@dlhbox + \fi \fi} + +% Also ok, but more sensitive to lookahead expansion is: +% +% \def\dontleavehmode{\ifvmode \indent \fi} +% +% which assumes indent is kept unchanged. Protecting the macro is only +% possible in etex (watch out: \unexpanded in context is eq to \protected). +% +% \unexpanded \def\dontleavehmode{\ifvmode \indent \fi} % functional spec TH + +%D But, if you run a recent version of \TEX, we can use the new +%D primitive: + +\ifx\normalquitvmode\undefined \else \let\dontleavehmode\normalquitvmode \fi + +%D \macros +%D {uppercasestring,lowercasestring} +%D +%D The names tell what they do: +%D +%D \starttyping +%D \uppercasestring somestring\to\somestring +%D \lowercasestring somestring\to\somestring +%D \stoptyping +%D +%D the first argument may be a \type{\macro}. + +\def\uppercasestring#1\to#2% first @EA redundant + {\edef#2{#1}\@EA\uppercase\@EA{\@EA\dodoglobal\@EA\edef\@EA#2\@EA{#2}}} + +\def\lowercasestring#1\to#2% first @EA redundant + {\edef#2{#1}\@EA\lowercase\@EA{\@EA\dodoglobal\@EA\edef\@EA#2\@EA{#2}}} + +%D \macros +%D {handletokens} +%D +%D With the next macro we enter a critical area of macro +%D expansion. What we want is a macro that looks like: +%D +%D \starttyping +%D \handletokens some tokens\with \somemacro +%D \stoptyping +%D +%D At first sight the next implementation will suffice, but +%D running this one shows that we loose the spaces. This is no +%D surprise because we grab arguments and spaces preceding those +%D are just ignored. +%D +%D \starttyping +%D \def\nohandletokens#1\end% +%D {} +%D +%D \def\dohandletokens#1#2\end% +%D {\ifx#1\endoftoken +%D \expandafter\nohandletokens +%D \else +%D \docommand{#1}% +%D \expandafter\dohandletokens +%D \fi +%D #2\end} +%D +%D \long\def\handletokens#1\with#2% +%D {\let\docommand=#2\relax +%D \dohandletokens#1\endoftoken\end} +%D \stoptyping +%D +%D A second approach therefore grabs the individual characters +%D by using \type{\afterassignment}, in which case the space is +%D read in as space. +%D +%D \starttyping +%D \def\dodohandletokens% +%D {\ifx\next\end \else +%D \docommand{\next}% +%D \expandafter\dohandletokens +%D \fi} +%D +%D \def\dohandletokens +%D {\afterassignment\dodohandletokens\let\next= } +%D +%D \long\def\handletokens#1\with#2% +%D {\let\docommand=#2% +%D \dohandletokens#1\end} +%D \stoptyping + +%D A bonus example: +%D +%D \starttyping +%D \hbox{\handletokens tekst en meer tekst\with\ruledhbox} +%D +%D \def\weetikveel#1{\if#1\blankspace\space\else\ruledhbox{#1}\fi} +%D +%D \hbox{\handletokens tekst en meer tekst\with\weetikveel} +%D \stoptyping + +%D \macros +%D {counttoken,counttokens} +%D +%D For the few occasions that we want to know the number of +%D specific tokens in a string, we can use: +%D +%D \starttyping +%D \counttoken token\in string\to \count +%D \counttokens string\to \count +%D \stoptyping +%D +%D This macro, that for instance is used in \type{cont-tab}, +%D takes a real counter. The macro can be preceded by \type +%D {\doglobal}. + +\def\counttoken#1\in#2\to#3% + {\redoglobal#3\zerocount + \def\!!stringa{#1}% + \def\!!stringb{\end}% + \def\docounttoken##1% obeys {} + {\def\!!stringc{##1}% + \ifx\!!stringb\!!stringc \else + \ifx\!!stringa\!!stringc + \dodoglobal\advance#3 \plusone + \fi + \expandafter\docounttoken + \fi}% + \docounttoken#2\end + \resetglobal} + +% \def\counttoken#1\in#2\to#3% +% {\redoglobal#3\zerocount +% \def\!!stringa{#1}% +% \def\docounttoken##1% obeys {} +% {\def\!!stringb{##1}% +% \ifx\!!stringa\!!stringb +% \dodoglobal\advance#3\plusone +% \fi}% +% \handletokens#1\with\docounttoken +% \resetglobal} + +\def\counttokens#1\to#2% + {\redoglobal#2\zerocount + \def\docounttoken##1{\dodoglobal\advance#2\plusone}% + \handletokens#1\with\docounttoken + \resetglobal} + +%D \macros +%D {splitofftokens} +%D +%D Running this one not always gives the expected results. +%D Consider for instance the macro for which I originally +%D wrote this token handler. + +\long\def\splitofftokens#1\from#2\to#3% + {\ifnum#1>\zerocount + \scratchcounter#1\relax + \def\dosplitofftokens##1% + {\ifnum\scratchcounter>\zerocount + \advance\scratchcounter \minusone + \edef#3{#3##1}% + \fi}% + % \let#3\empty % #3 can be #2, so: + \@EA\let\@EA#3\@EA\empty + \@EA\handletokens#2\with\dosplitofftokens + \else + \edef#3{#2}% + \fi} + +%D This macro can be called like: +%D +%D \startbuffer[example] +%D \splitofftokens10\from01234567 890123456789\to\test [\test] +%D \stopbuffer +%D +%D However, the characters that we expect to find in +%D \type{\test} just don;t show up there. The reason for this +%D is not that logical but follows from \TEX's sometimes +%D mysterious way of expanding. Look at this: +%D +%D \startbuffer[next] +%D \def\next{a} \edef\test{\next} [\test] +%D \let\next=b \edef\test{\test\next} [\test] +%D \let\next=c \edef\test{\next} [\test] +%D \let\next=d \edef\test{\test\next} [\test] +%D \let\next=e \@EA\edef\@EA\test\@EA{\test\next} [\test] +%D \stopbuffer +%D +%D \typebuffer[next] +%D +%D Careful reading shows that inside an \type{\edef} macro's +%D that are \type{\let} are not expanded! +%D +%D \unprotect\getbuffer[next]\protect +%D +%D That's why we finally end up with a macro that looks +%D ahead by using an assignment, this time by using \type +%D {\futurelet}, and grabbing an argument as well. That +%D way we can handle the sentinal, a blank space and grouped +%D tokens. + +\def\dohandletokens % \nexthandledtoken is part of interface + {\futurelet\nexthandledtoken\dodohandletokens} + +\long\def\handletokens#1\with#2% + {\gdef\dododohandletokens{#2}% permits more complex #2's + \dohandletokens#1\end} + +%D A previous version said \type{\docommand=#2}, but to enable +%D use in alignments, I decided to use another placeholder, one +%D that is not sensitive to the global assignment. + +%D This alternatives does not handle grouped tokens well, so +%D next we had (for a short moment): +%D +%D \starttyping +%D \def\dodohandletokens#1% +%D {\ifx\nexthandledtoken\blankspace +%D \dododohandletokens{ }% +%D \fi +%D \ifx#1\end \else +%D \dododohandletokens{#1}% +%D \expandafter\dohandletokens +%D \fi} +%D \stoptyping +%D +%D This one failed on a trailing space, something we +%D encounter in \JAVASCRIPT\ cleaning. +%D +%D \starttyping +%D \def\dodohandletokens#1% +%D {\ifx\nexthandledtoken\blankspace +%D \dododohandletokens{ }% +%D \fi +%D \ifx\nexthandledtoken\end \else +%D \dododohandletokens{#1}% +%D \expandafter\dohandletokens +%D \fi} +%D \stoptyping +%D +%D So, now we have: + +\def\dodohandletokens % can be sped up + {\ifx\nexthandledtoken\blankspace + \def\next * {\dododohandletokens{ }\dohandletokens}% + \else\ifx\nexthandledtoken\end + \let\next\gobbletwoarguments + \else + \long\def\next *##1{\dododohandletokens{##1}\dohandletokens}% + \fi\fi + \next *} + +%D This macro is tested on: +%D +%D \def\xxx#1{[#1]} +%D +%D \startlines +%D \handletokens abc\with\xxx +%D \handletokens a b c\with\xxx +%D \handletokens a b c\with\xxx +%D \handletokens a{bc}d\with\xxx +%D \handletokens a\space bc \with\xxx +%D \stoplines +%D +%D And our previous example shows up as: +%D +%D \getbuffer[example] + +%D \macros +%D {iftrialtypesetting, ifvisible} +%D +%D The next boolean is at first sight a strange one. Sometimes +%D one does a trial typesetting run, for instance to determine +%D dimensions. Some mechanisms, like object inclusion, can fail +%D on such trials. Temporary setting the next boolean to true, +%D helps a lot. The second boolena can be used to inhibit +%D processing completely. + +\newif\iftrialtypesetting \trialtypesettingfalse +\newif\ifvisible \visibletrue + +%D \macros +%D {startlocal, startglobal} +%D +%D The next four macros are rather self explaining: +%D +%D \starttyping +%D \startlocal +%D whatever assignments +%D \stoplocal +%D +%D \startglobal +%D whatever assignments +%D \stopglobal +%D \stoptyping +%D +%D These macros are meant for those who know the difference +%D between local and global assignments and are aware of the +%D possible unwanted side effect + +\def\dostartglobaldefs#1#2% + {\edef\!!stringa{\the\globaldefs}% + \ifnum\globaldefs#10 + \globaldefs-\globaldefs + \fi + \advance\globaldefs#21 + \letvalue{@gd@\the\globaldefs}\!!stringa} + +\def\dostopglobaldefs% + {\doifdefinedelse{@gd@\the\globaldefs} + {\globaldefs\getvalue{@gd@\the\globaldefs}\relax} + {\globaldefs\zerocount}} + +\def\startlocal {\dostartglobaldefs>-} +\def\stoplocal {\dostopglobaldefs} +\def\startglobal {\dostartglobaldefs<+} +\def\stopglobal {\dostopglobaldefs} + +%D \macros +%D {twodigitrounding} +%D +%D When using \type {\special}s or \type {\pdfliteral}s, it +%D sometimes makes sense to limit the precission. The next +%D macro rounds a real number to two digits. It takes one +%D argument and only works in \ETEX. + +\beginTEX + + \let\integerrounding \firstofoneargument + \let\onedigitrounding \firstofoneargument + \let\twodigitrounding \firstofoneargument + \let\threedigitrounding\firstofoneargument + +\endTEX + +\beginETEX \dimexpr + + \def\dointegerrounding #1.#2\relax {#1} + \def\doonedigitrounding #1.#2#3\relax {\ifx#2*#1\else#1.#2\fi} + \def\dotwodigitrounding #1.#2#3#4\relax {\ifx#2*#1\else#1.#2#3\fi} + \def\dothreedigitrounding#1.#2#3#4#5\relax{\ifx#2*#1\else#1.#2#3#4\fi} + + \def\integerrounding#1% + {\@EA\@EA\@EA\dointegerrounding \@EA\WITHOUTPT\the\dimexpr#1\points+.5\points \relax .\relax} + \def\onedigitrounding#1% + {\@EA\@EA\@EA\doonedigitrounding \@EA\WITHOUTPT\the\dimexpr#1\points+.05\points \relax 00.*0\relax} + \def\twodigitrounding#1% + {\@EA\@EA\@EA\dotwodigitrounding \@EA\WITHOUTPT\the\dimexpr#1\points+.005\points \relax 000.*00\relax} + \def\threedigitrounding#1% + {\@EA\@EA\@EA\dothreedigitrounding\@EA\WITHOUTPT\the\dimexpr#1\points+.0005\points\relax0000.*00\relax} + +% \def\dointegerrounding #1.#2\relax {#1} +% \def\doonedigitrounding #1.#2#3\relax {#1.#2} +% \def\dotwodigitrounding #1.#2#3#4\relax {#1.#2#3} +% \def\dothreedigitrounding#1.#2#3#4#5\relax{#1.#2#3#4} + +% \def\integerrounding #1{\@EA\@EA\@EA\dointegerrounding \@EA\WITHOUTPT\the\dimexpr #1\points+.5\points\relax \relax} +% \def\onedigitrounding #1{\@EA\@EA\@EA\doonedigitrounding \@EA\WITHOUTPT\the\dimexpr #1\points+.05\points\relax 0\relax} +% \def\twodigitrounding #1{\@EA\@EA\@EA\dotwodigitrounding \@EA\WITHOUTPT\the\dimexpr #1\points+.005\points\relax 00\relax} +% \def\threedigitrounding#1{\@EA\@EA\@EA\dothreedigitrounding\@EA\WITHOUTPT\the\dimexpr#1\points+.0005\points\relax000\relax} + +% \def\integerroundeddimen #1{\@EA\@EA\@EA\dointegerrounding \@EA\WITHOUTPT\the\dimexpr #1+.5\points\relax \relax} +% \def\onedigitroundeddimen #1{\@EA\@EA\@EA\doonedigitrounding \@EA\WITHOUTPT\the\dimexpr #1+.05\points\relax 0\relax} +% \def\twodigitroundeddimen #1{\@EA\@EA\@EA\dotwodigitrounding \@EA\WITHOUTPT\the\dimexpr #1+.005\points\relax 00\relax} +% \def\threedigitroundeddimen#1{\@EA\@EA\@EA\dothreedigitrounding\@EA\WITHOUTPT\the\dimexpr#1+.0005\points\relax000\relax} + +\endETEX + +%D \macros +%D {processcontent} +%D +%D This is the first occasion where \TEX\ and \ETEX\ are no +%D longer compatible, although in many cases things go ok. +%D Beware of verbatim, i.e. catcode changes. +%D +%D \starttyping +%D \def\starthans% +%D {\processcontent{stophans}\test{\message{\test}\wait}} +%D \stoptyping +%D +%D This macro is first used in the tabulation macros. + +\def\processcontent#1% + {\begingroup\@EA\doprocesscontent\csname#1\endcsname} + +%\beginTEX + +\def\doprocesscontent#1#2#3% + {\long\def\doprocesscontent##1#1% + {\endgroup\long\def#2{##1}#3}% + \doprocesscontent} + +%\endTEX + +% Hm. Side effect, spaces after \type{\test} in verbatim. +% must set eof token + +%\beginETEX \scantokens +% +%\def\doprocesscontent#1#2#3% +% {\long\def\doprocesscontent##1#1% +% {\egroup\long\def#2{\scantokens{##1}}#3}% +% \doprocesscontent} +% +%\endETEX + +%D \macros +%D {dogobblesingleempty, dogobbledoubleempty} +%D +%D These two macros savely grab and dispose two arguments. + +\def\dogobblesingleempty{\dosingleempty\dodogobblesingleempty} +\def\dogobbledoubleempty{\dodoubleempty\dodogobbledoubleempty} + +\def\dodogobblesingleempty [#1]{} +\def\dodogobbledoubleempty[#1][#2]{} + +\let\gobblesingleempty\dogobblesingleempty % also used +\let\gobbledoubleempty\dogobbledoubleempty % also used + +%D \macros +%D {sortcommalist,sortcommacommand, +%D donumericcompare,comparedresult} +%D +%D Sometimes we need to sort a commalist, so here is Taco's +%D solution. This will in many cases be a list that is stored +%D in a \type{\csname}, so both commalist and commacommands are +%D supported. The sorting algorithm is very simple, so the list +%D should not be too long or sorting will be very slow. +%D +%D \starttyping +%D \sortcommalist[10,2,4,5,6,1,2,3,4,10,20]\donumericcompare +%D +%D \def\test{10,2,4,5,6,1,2,3,4,10,20} +%D +%D \sortcommacommand[\test]\donumericcompare +%D \stoptyping +%D +%D In both cases, the result is available in the macro \type +%D {\sortedcommalist}. +%D +%D Parameter \type{#2} is a macro that should accept two +%D parameters, and it has to decide which one is larger, by +%D setting the counter \type{\comparedresult} to~0 (for equal), +%D 1~(if it's first argument is larger), or~2 (if it's second +%D argument is larger). +%D +%D As said, these macro are largely written by Taco, and are +%D (maybe therefore) also the first application of \type +%D {\replaceincommalist}. + +\newcount\comparedresult + +\def\sortcommacommand[#1]% + {\@EA\sortcommalist\@EA[#1]} + +\def\sortcommalist[#1]#2% + {\getcommalistsize[#1]% + \ifnum\commalistsize>1 + \let\sortedcommalist\empty + \let\comparecommand#2% + \processcommalist[#1]\dosortcommacommand + \else + \def\sortedcommalist{#1}% + \fi} + +\def\dosortcommacommand#1% + {\ifx\sortedcommalist\empty + \def\sortedcommalist{#1}% + \else + \def\!!tempa{#1}% + \ifx\!!tempa\empty\else + \scratchcounter\plusone + \@EA\getcommalistsize\@EA[\sortedcommalist]% + \@EA\processcommalist\@EA[\sortedcommalist]\docompareitems + \fi + \fi} + +%D All those \type{\expandafter}'s are there because I do not +%D want to use \type{\edef}. + +\def\docompareitems#1% + {\doifnotempty{#1} + {\@EA\comparecommand\@EA{\!!tempa}{#1}\relax + %\ifcase\compareresult % equal + \ifnum\comparedresult<2 + \ifnum\scratchcounter=\commalistsize + \@EA\@EA\@EA\def\@EA\@EA\@EA\sortedcommalist + \@EA\@EA\@EA{\@EA\sortedcommalist\@EA,\!!tempa}% + \fi + %\or % new element larger + % \ifnum\scratchcounter=\commalistsize + % \@EA\@EA\@EA\def\@EA\@EA\@EA\sortedcommalist + % \@EA\@EA\@EA{\@EA\sortedcommalist\@EA,\!!tempa}% + % \fi + \else % old element larger + \@EA\def\@EA\newcommalistelement\@EA{\!!tempa,#1}% + \replaceincommalist\sortedcommalist\scratchcounter + \expandafter\quitcommalist + \fi}% + \advance\scratchcounter \plusone} % bug, was \minusone + +%D The macro \type{\donumericcompare} considers everything +%D that is not a number to be larger than any number. + +\def\donumericcompare#1#2% + {\doifnumberelse{#1} + {\doifnumberelse{#2} + {\ifnum#1>#2\relax + \comparedresult\plusone % #1 is larger + \else\ifnum#1<#2\relax + \comparedresult\plustwo % #2 is larger + \else + \comparedresult\zerocount % both are equal + \fi\fi} + {\comparedresult\plustwo}} + {\comparedresult\plusone}} + +%D \macros +%D {@True, @False, @Not, @And} +%D +%D Some predicate logic functions, used in for instance the +%D math module. + +\def\@True {00} +\def\@False {01} +\def\@Not #1{0\ifcase#11 \or\@EA 1\else \@EA 0\fi} +\def\@And #1#2{0\ifcase#1#2 \@EA 0\else \@EA 1\fi} + +%D \macros +%D {setdimensionwithunit, freezedimensionwithunit} +%D +%D The next assignments are all valid: +%D +%D \starttyping +%D \setdimensionwithunit\scratchdimen{10} {cm} +%D \setdimensionwithunit\scratchdimen{10cm}{cm} +%D \setdimensionwithunit\scratchdimen{10cm}{} +%D \freezedimensionwithunit\SomeWidth{\textwidth} +%D \freezedimensionwithunit\SomeDepth{\dp\strutbox} +%D \stoptyping +%D +%D As an alternative for the next macro we can use a global +%D assignment inside a box. The \type{\empty}'s permits +%D gobbling while preventing spurious \type{\relax}'s. + +\def\setdimensionwithunit#1#2#3% number unit dimension / nice trick + {\afterassignment\gobblefourarguments#1=#2#3pt\relax\empty\empty\empty\empty} + +\def\freezedimensionwithunit#1#2% + {\setdimensionwithunit\scratchdimen#1{#2}\edef#1{\the\scratchdimen}} + +%D \macros +%D {doifsometokselse} +%D +%D Not that fast I guess, but here's a way to test for token +%D registers being empty. + +\def\doifsometokselse#1% % #2#3% + {\edef\!!stringa{\the#1}% + \ifx\!!stringa\empty % #3\else#2\fi} + \expandafter\secondoftwoarguments + \else + \expandafter\firstoftwoarguments + \fi} + +%D \macros +%D {startstrictinspectnextcharacter} +%D +%D This one if for Taco's bibliography module: + +\let\normalinspectnextcharacter\inspectnextcharacter + +\def\strictinspectnextcharacter% no user macro ! + {\ifx\nexttoken\charactertoken + \expandafter\!!stringa + \else + \expandafter\!!stringb + \fi} + +% better: push/pop + +\def\startstrictinspectnextcharacter + {\let\inspectnextcharacter\strictinspectnextcharacter} + +\def\stopstrictinspectnextcharacter + {\let\inspectnextcharacter\normalinspectnextcharacter} + +%D \macros +%D {gobblespacetokens} +%D +%D This macro needs a speed-up! + +%\def\gobblespacetokens +% {\doifnextcharelse\empty\donothing\donothing} % no {}\do\do ! + +\def\gobblespacetokens + {\afterassignment\nexttoken\let\nexttoken=} + +%D \macros +%D {verbatimargument} +%D +%D As the name says, this macro converts its argument to a +%D (rather safe) string. + +\def\verbatimstring#1% + {\convertargument#1\to\ascii\ascii} + +%D These are needed in ordinal number conversions: + +\def\lastdigit#1% + {\@EA\thelastdigit\number#1\relax} + +\def\thelastdigit#1#2% + {\ifx#2\relax#1\else\@EA\thelastdigit\@EA#2\fi} + +\def\lasttwodigits#1% + {\@EA\thelasttwodigits\@EA0\number#1\relax} + +\def\thelasttwodigits#1#2#3% 0 dig ... \relax + {\ifx#3\relax#1#2\else\@EA\thelasttwodigits\@EA#2\@EA#3\fi} + +%D \macros +%D {serializecommalist} +%D +%D Concatenate commalists: + +\def\serializecommalist[#1]% + {\let\serializedcommalist\empty + \def\docommand##1{\edef\serializedcommalist{\serializedcommalist##1}}% + \processcommacommand[#1]\docommand} + +%D \macros +%D {purenumber} +%D +%D Sometimes we need control over when \TEX\ stops reading a +%D number, especially in full expandable macros where using +%D \type {\relax} would lead to disasters. +%D +%D \starttyping +%D \ifodd\purenumber{...}\space ... \else ... \fi +%D \stoptyping +%D +%D Here we use a space as number delimiter in combination +%D with a space- and relax-less \type {\purenumber}. This +%D macro works ok with \type {\the}, \type {\number} as well +%D as \ETEX's \type {\numexpr}. + +\def\purenumber#1{\@EA\firstofoneargument\@EA{\number#1}} + +%D \macros +%D {filterfromvalue} +%D +%D \starttyping +%D \setvalue{xx}{{A}{B}{C}} +%D +%D \filterfromvalue{xx}{3}{3} +%D \filterfromvalue{xx}{3}{2} +%D \filterfromvalue{xx}{3}{1} +%D \stoptyping +%D +%D An alternative is to store 'max' in the list, say: +%D +%D \starttyping +%D \setvalue{xx}{3{A}{B}{C}} +%D +%D \filterfromvalues{3}{xx}{3} +%D \filterfromvalues{3}{xx}{2} +%D \filterfromvalues{3}{xx}{1} +%D \stoptyping +%D +%D I'll implement this when I'm in \quotation {writing dirty +%D macros mood}. + +\def\dofilterfromstr#1#2% max n + {\ifcase#1\or + \ifcase#2\or + \strippedcsname\firstofoneargument + \else + \strippedcsname\gobbleoneargument + \fi + \or + \ifcase#2\or + \strippedcsname\firstoftwoarguments + \or + \strippedcsname\secondoftwoarguments + \else + \strippedcsname\gobbletwoarguments + \fi + \or + \ifcase#2\or + \strippedcsname\firstofthreearguments + \or + \strippedcsname\secondofthreearguments + \or + \strippedcsname\thirdofthreearguments + \else + \strippedcsname\gobblethreearguments + \fi + \or + \ifcase#2\or + \strippedcsname\firstoffourarguments + \or + \strippedcsname\secondoffourarguments + \or + \strippedcsname\thirdoffourarguments + \or + \strippedcsname\fourthoffourarguments + \else + \strippedcsname\gobblefourarguments + \fi + \or + \ifcase#2\or + \strippedcsname\firstoffivearguments + \or + \strippedcsname\secondoffivearguments + \or + \strippedcsname\thirdoffivearguments + \or + \strippedcsname\fourthoffivearguments + \or + \strippedcsname\fifthoffivearguments + \else + \strippedcsname\gobblefivearguments + \fi + \fi} + +\def\filterfromvalue#1#2#3% value max n + {\@EA\@EAEAEA\csname % we use the fact that an + \@EA\ifx\csname#1\endcsname\relax % undefined cs has become \relax + \strippedcsname\gobbleoneargument % which we then gobble here + \else + \dofilterfromstr{#2}{#3}% + \fi + \endcsname\csname#1\endcsname} + +\def\filterfromnext#1#2% max n {..}{..}{..}{..} + {\csname\dofilterfromstr{#1}{#2}\endcsname} + +%D \macros +%D {definemeasure} +%D +%D \starttyping +%D \definemeasure[mywidth][\dimexpr(\textwidth-1cm)] +%D +%D ... \measure{mywidth} ... +%D \stoptyping + +\def\??dm{@@dm} % brrr + +\def\definemeasure + {\dodoubleargument\dodefinemeasure} + +\def\dodefinemeasure[#1][#2]% + {\setvalue{\??dm#1}{#2}} + +% #2 could be omitted, but we want to support spaces +% +% \setmeasure {x} {1cm} +% \setmeasure {xx} {1cm} +% \setmeasure {xxx}{1cm} + +\def\setmeasure #1#2{\setvalue{\??dm#1}{#2}} % quick way +\def\setemeasure#1#2{\setevalue{\??dm#1}{#2}} % quick way +\def\setgmeasure#1#2{\setgvalue{\??dm#1}{#2}} % quick way +\def\setxmeasure#1#2{\setxvalue{\??dm#1}{#2}} % quick way + +\def\measure#1% + {\ifcsname\??dm#1\endcsname\csname\??dm#1\endcsname\else\zeropoint\fi} + +%D \macros +%D {doifdimensionelse} +%D +%D This is a dirty one: we simply append a unit and discard it when needed. + +\def\doifdimensionelse#1% + {\afterassignment\dodoifdimensionelse\scratchdimen#1pt\relax} + +\def\dodoifdimensionelse#1% + {\ifx#1\relax + \expandafter\secondoftwoarguments + \else % #1=p ... t\relax + \expandafter\thirdoffourarguments + \fi} + +%D \macros +%D {comparedimension,comparedimensioneps} +%D +%D This is a dirty one: we simply append a unit and discard it when needed. + +\newdimen\roundingeps \roundingeps=10sp + +\def\comparedimension#1#2% + {\chardef\compresult + \ifdim#1<#2% + \zerocount + \else\ifdim#1<#2% + \plusone + \else + \plustwo + \fi\fi} + +\beginETEX + +\def\comparedimensioneps#1#2% + {\chardef\compresult + \ifdim\dimexpr(#1-#2)<\roudingeps + \zerocount + \else\ifdim\dimexpr(#2-#1)<\roudingeps + \zerocount + \else\ifdim#1<#2% + \plusone + \else + \plustwo + \fi\fi\fi} + +\endETEX + +\beginTEX + +\newdimen\comparedimen + +\def\comparedimensioneps#1#2% + {\comparedimen#1\advance\comparedimen-#2\relax + \chardef\compresult + \ifdim\comparedimen<\roudingeps + \zerocount + \else\ifdim-\comparedimen<\roudingeps + \zerocount + \else\ifdim#1<#2% + \plusone + \else + \plustwo + \fi\fi\fi} + +\endTEX + +\protect \endinput |