diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/syst-aux.mkiv')
-rw-r--r-- | Master/texmf-dist/tex/context/base/syst-aux.mkiv | 6914 |
1 files changed, 6914 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/syst-aux.mkiv b/Master/texmf-dist/tex/context/base/syst-aux.mkiv new file mode 100644 index 00000000000..995a0cb6e02 --- /dev/null +++ b/Master/texmf-dist/tex/context/base/syst-aux.mkiv @@ -0,0 +1,6914 @@ +%D \module +%D [ file=syst-gen, +%D version=1996.03.20, +%D title=\CONTEXT\ System Macros, +%D subtitle=General, +%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. + +%D Some of the macros will move to syst-obs as they might become +%D obsolete once we've redone the bibliography module. Of course +%D the handy helpers will stay. + +%D This is a stripped down combination of: +%D +%D \startitemize +%D \item \type {syst-gen.tex} +%D \item \type {syst-ext.tex} +%D \item \type {syst-new.tex} +%D \stopitemize +%D +%D We keep them around (for \MKII) so you can find comments, +%D experiences, intermediate versions and cleaner variants +%D there (and also non-\ETEX\ variants). +%D +%D Contrary to the older files, we now assume that this one +%D is used in \CONTEXT\ and therefore we might also assume that +%D some basic functionality is available. +%D +%D Some of the macros here are used in the bibliography module. They +%D will be moved to a separate syst module some once the bib module +%D is made \MKIV. + +\unprotect + +\let\reportprotectionstate\relax + +%D \macros +%D {doifolderversionelse} +%D +%D We start with a macro specially for Aditya who wants to be able +%D to use development versions of \MKIV\ for real documents. +%D +%D \starttyping +%D \doifolderversionelse\contextversion{2001.02.03}{yes}{no} +%D \doifolderversionelse\contextversion{3001.02.03}{yes}{no} +%D \stoptyping +%D +%D The \type {yyyy.mm.dd} syntax is rather strict. + +\def\@@versiontonumber#1.#2.#3#4#5\relax + {\numexpr#1*\plustenthousand+#2*\plushundred+#3#4\relax} + +\def\doifolderversionelse#1#2% + {\normalexpanded{\noexpand\ifnum\noexpand\@@versiontonumber#1\relax<\noexpand\@@versiontonumber#2\relax}\relax + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +%D \macros +%D {normalspace} +%D +%D There is already \type{\space} but just to be sure we also +%D provide: + +\def\normalspace{ } + +%D \macros +%D {!!count, !!toks, !!dimen, !!box, +%D !!width, !!height, !!depth, !!string, !!done} +%D +%D We define some more \COUNTERS\ and \DIMENSIONS. We also +%D define some shortcuts to the local scatchregisters~0, 2, 4, +%D 6 and~8. + +\newcount\!!counta \newtoks\!!toksa \newdimen\!!dimena \newbox\!!boxa +\newcount\!!countb \newtoks\!!toksb \newdimen\!!dimenb \newbox\!!boxb +\newcount\!!countc \newtoks\!!toksc \newdimen\!!dimenc \newbox\!!boxc +\newcount\!!countd \newtoks\!!toksd \newdimen\!!dimend \newbox\!!boxd +\newcount\!!counte \newtoks\!!tokse \newdimen\!!dimene \newbox\!!boxe +\newcount\!!countf \newtoks\!!toksf \newdimen\!!dimenf \newbox\!!boxf + \newdimen\!!dimeng + \newdimen\!!dimenh + \newdimen\!!dimeni + \newdimen\!!dimenj + \newdimen\!!dimenk + +\let\!!stringa\empty \let\!!stringb\empty \let\!!stringc\empty +\let\!!stringd\empty \let\!!stringe\empty \let\!!stringf\empty + +\newdimen\!!widtha \newdimen\!!heighta \newdimen\!!deptha +\newdimen\!!widthb \newdimen\!!heightb \newdimen\!!depthb +\newdimen\!!widthc \newdimen\!!heightc \newdimen\!!depthc +\newdimen\!!widthd \newdimen\!!heightd \newdimen\!!depthd + +\newif\if!!donea \newif\if!!doneb \newif\if!!donec +\newif\if!!doned \newif\if!!donee \newif\if!!donef + +\def\!!zerocount {0} % alongside \zerocount +\def\!!minusone {-1} % alongside \minusone +\def\!!plusone {1} % alongside \plusone +\def\!!plustwo {2} % alongside \plustwo +\def\!!plusthree {3} % alongside \plusthree + +\ifdefined\data \else \let\data \relax \fi % dep checker + +%D \macros +%D {s!,c!,e!,p!,v!,@@,??} +%D +%D To save memory, we use constants (sometimes called +%D variables). Redefining these constants can have disastrous +%D results. + +\def\v!prefix! {v!} \def\c!prefix! {c!} +\def\s!prefix! {s!} \def\p!prefix! {p!} + +\def\s!next {next} \def\s!default {default} +\def\s!dummy {dummy} \def\s!unknown {unknown} + +\def\s!do {do} \def\s!dodo {dodo} + +\def\s!complex {complex} \def\s!start {start} +\def\s!simple {simple} \def\s!stop {stop} + +\def\s!empty {empty} + +%D \macros +%D {@EA,@EAEA,@EAEAEA,@EAEAEAEAEAEA,expanded,startexpanded} +%D +%D When in unprotected mode, to be entered with +%D \type{\unprotect}, one can use \type{\@EA} as equivalent +%D of \type{\expandafter}. + +\let\@NX\noexpand +\let\@EA\expandafter + +\def\@EAEA {\expandafter\expandafter} +\def\@EAEAEA{\expandafter\expandafter\expandafter} + +\def\@EAEAEAEAEAEA{\expandafter\@EAEAEA\expandafter} + +%D Sometimes we pass macros as arguments to commands that +%D don't expand them before interpretation. Such commands can +%D be enclosed with \type{\expanded}, like: +%D +%D \starttyping +%D \expanded{\setupsomething[\alfa]} +%D \stoptyping +%D +%D Such situations occur for instance when \type{\alfa} is a +%D commalist or when data stored in macros is fed to index of +%D list commands. If needed, one should use \type{\noexpand} +%D inside the argument. Later on we will meet some more clever +%D alternatives to this command. + +\long\def\@@expanded{} % always long; global (less restores) + +\long\def\expanded#1% + {\long\xdef\@@expanded{\noexpand#1}\@@expanded} + +%D Beware, the next one has no \type {\noexpand} before its +%D argument. + +\long\def\startexpanded#1\stopexpanded % see x-fo for example + {\long\xdef\@@expanded{#1}\@@expanded} + +%D Recent \TEX's have a primitive \expanded + +% \long\def\expanded +% {\normalexpanded\bgroup\noexpand\gobblenexttoken} + +%D \macros +%D {safeexpanded,everysafeexpanded} +%D +%D In addition we provide: + +\newtoks\everysafeexpanded + +\long\def\safeexpanded#1% why the \noexpand + {\begingroup + \the\everysafeexpanded\long\xdef\@@expanded{\noexpand#1}% + \endgroup + \@@expanded} + +\def\safeedef#1#2% + {\begingroup + \the\everysafeexpanded\long\xdef\@@expanded{\noexpand#2}% + \endgroup + \let#1\@@expanded} + +\def\safexdef#1#2% + {\begingroup + \the\everysafeexpanded\long\xdef\@@expanded{\noexpand#2}% + \endgroup + \global\let#1\@@expanded} + +%D You can append protective measures to the token register if +%D needed, as we will do later. + +%D \macros +%D {expandoneargafter,expandtwoargsafter} +%D +%D These two commands make macros more readable by hiding a +%D lot of \type {\expandafter}'s. They expand the arguments +%D after the first command. +%D +%D \starttyping +%D \expandoneargafter \command{\abc} +%D \expandtwoargsafter\command{\abc}{\def} +%D \stoptyping +%D +%D These commands expect the arguments to be macros. + +\def\expandoneargafter #1{\@EA#1\@EA} +\def\expandtwoargsafter#1#2{\@EA\@EA\@EA#1\@EA\@EA\@EA{\@EA#2\@EA}\@EA} + +%D These two do a full expansion: + +\def\fullexpandoneargafter #1#2{\long\xdef\@@expanded{\noexpand#1{#2}}\@@expanded} +\def\fullexpandtwoargsafter#1#2#3{\long\xdef\@@expanded{\noexpand#1{#2}{#3}}\@@expanded} + +%D \macros +%D {gobbleoneargument,gobble...arguments} +%D +%D The next set of macros just do nothing, except that they +%D get rid of a number of arguments. + +\long\def\gobbleoneargument #1{} +\long\def\gobbletwoarguments #1#2{} +\long\def\gobblethreearguments #1#2#3{} +\long\def\gobblefourarguments #1#2#3#4{} +\long\def\gobblefivearguments #1#2#3#4#5{} +\long\def\gobblesixarguments #1#2#3#4#5#6{} +\long\def\gobblesevenarguments #1#2#3#4#5#6#7{} +\long\def\gobbleeightarguments #1#2#3#4#5#6#7#8{} +\long\def\gobbleninearguments #1#2#3#4#5#6#7#8#9{} +\long\def\gobbletenarguments #1{\gobbleninearguments} + +%D \macros +%D {doifnextcharelse} +%D +%D When we started using \TEX\ in the late eighties, our +%D first experiences with programming concerned a simple shell +%D around \LATEX. The commands probably use most at \PRAGMA, +%D are the itemizing ones. One of those few shell commands took +%D care of an optional argument, that enabled us to specify +%D what kind of item symbol we wanted. Without understanding +%D anything we were able to locate a \LATEX\ macro that could +%D be used to inspect the next character. +%D +%D It's this macro that the ancester of the next one presented +%D here. It executes one of two actions, dependant of the next +%D character. Disturbing spaces and line endings, which are +%D normally interpreted as spaces too, are skipped. +%D +%D \starttyping +%D \doifnextcharelse {karakter} {then ...} {else ...} +%D \stoptyping +%D +%D This macro differs from the original in the use of \type +%D {\localnext} because we don't want clashes with \type +%D {\next}. + +\long\def\doifnextcharelse#1#2#3% #1 should not be {} ! + {\let\charactertoken=#1% = needed here + \def\!!stringa{#2}% + \def\!!stringb{#3}% + \futurelet\nexttoken\inspectnextcharacter} + +\def\inspectnextcharacter + {\ifx\nexttoken\blankspace + \@EA\reinspectnextcharacter + \else + \@EA\inspectnextcharacterindeed + \fi} +\def\inspectnextcharacterindeed + {\ifx\nexttoken\charactertoken + \@EA\!!stringa + \else + \@EA\!!stringb + \fi} + +%D Because we will mostly use this macro for testing if the next +%D character is \type {[}, we also make a slightly faster variant +%D as it is not uncommon to have tens of thousands of calls to this +%D test in a run. Of course it also is more convenient to read a +%D trace then. + +\let\nextoptionalcharactertoken=[ + +\long\def\doifnextoptionalelse#1#2% + {\def\nextoptionalcommandyes{#1}% + \def\nextoptionalcommandnop{#2}% + \futurelet\nexttoken\inspectnextoptionalcharacter} +\def\inspectnextoptionalcharacter + {\ifx\nexttoken\blankspace + \@EA\reinspectnextoptionalcharacter + \else + \@EA\inspectnextoptionalcharacterindeed + \fi} +\def\inspectnextoptionalcharacterindeed + {\ifx\nexttoken\nextoptionalcharactertoken + \@EA\nextoptionalcommandyes + \else + \@EA\nextoptionalcommandnop + \fi} + +\let\nextbgroupcharactertoken\bgroup + +\long\def\doifnextbgroupelse#1#2% + {\def\nextbgroupcommandyes{#1}% + \def\nextbgroupcommandnop{#2}% + \futurelet\nexttoken\inspectnextbgroupcharacter} +\def\inspectnextbgroupcharacter + {\ifx\nexttoken\blankspace + \@EA\reinspectnextbgroupcharacter + \else + \@EA\inspectnextbgroupcharacterindeed + \fi} +\def\inspectnextbgroupcharacterindeed + {\ifx\nexttoken\nextbgroupcharactertoken + \@EA\nextbgroupcommandyes + \else + \@EA\nextbgroupcommandnop + \fi} + +\let\nextparenthesischaractertoken( + +\long\def\doifnextparenthesiselse#1#2% + {\def\nextparenthesiscommandyes{#1}% + \def\nextparenthesiscommandnop{#2}% + \futurelet\nexttoken\inspectnextparenthesischaracter} +\def\inspectnextparenthesischaracter + {\ifx\nexttoken\blankspace + \@EA\reinspectnextparenthesischaracter + \else + \@EA\inspectnextparenthesischaracterindeed + \fi} +\def\inspectnextparenthesischaracterindeed + {\ifx\nexttoken\nextparenthesischaractertoken + \@EA\nextparenthesiscommandyes + \else + \@EA\nextparenthesiscommandnop + \fi} + +%D The next one is handy in predictable situations: + +\long\def\doiffastoptionalcheckelse#1#2% + {\def\nextoptionalcommandyes{#1}% + \def\nextoptionalcommandnop{#2}% + \futurelet\nexttoken\dodoiffastoptionalcheckelse} + +\def\dodoiffastoptionalcheckelse + {\ifx\nexttoken\nextoptionalcharactertoken + \expandafter\nextoptionalcommandyes + \else + \expandafter\nextoptionalcommandnop + \fi} + +%D This macro uses some auxiliary macros. Although we were able +%D to program quite complicated things, I only understood these +%D after rereading the \TEX book. The trick is in using a +%D command with a one character name. Such commands differ from +%D the longer ones in the fact that trailing spaces are {\em +%D not} skipped. This enables us to indirectly define a long +%D named macro that gobbles a space. +%D +%D In the first line we define \type{\blankspace}. Next we +%D make \type{\:} equivalent to \type{\reinspect...}. This +%D one||character command is expanded before the next +%D \type{\def} comes into action. This way the space after +%D \type{\:} becomes a delimiter of the longer named +%D \type{\reinspectnextcharacter}. + +\let\next\: + +\def\:{\let\blankspace= } \: + +\def\:{\reinspectnextcharacter} +\expandafter\def\: {\futurelet\nexttoken\inspectnextcharacter} + +\def\:{\reinspectnextoptionalcharacter} +\expandafter\def\: {\futurelet\nexttoken\inspectnextoptionalcharacter} + +\def\:{\reinspectnextbgroupcharacter} +\expandafter\def\: {\futurelet\nexttoken\inspectnextbgroupcharacter} + +\let\:\next + +%D \macros +%D {setvalue,setgvalue,setevalue,setxvalue, +%D letvalue,letgvalue,getvalue,resetvalue, +%D undefinevalue,ignorevalue} +%D +%D \TEX's primitive \type{\csname} can be used to construct +%D all kind of commands that cannot be defined with +%D \type{\def} and \type{\let}. Every macro programmer sooner +%D or later wants macros like these. +%D +%D \starttyping +%D \setvalue {name}{...} = \def\name{...} +%D \setgvalue {name}{...} = \gdef\name{...} +%D \setevalue {name}{...} = \edef\name{...} +%D \setxvalue {name}{...} = \xdef\name{...} +%D \letvalue {name}=\... = \let\name=\... +%D \letgvalue {name}=\... = \global\let\name=\... +%D \getvalue {name} = \name +%D \resetvalue {name} = \def\name{} +%D \stoptyping +%D +%D As we will see, \CONTEXT\ uses these commands many times, +%D which is mainly due to its object oriented and parameter +%D driven character. + +\def\setvalue #1{\expandafter \def\csname#1\endcsname} +\def\setgvalue #1{\expandafter\gdef\csname#1\endcsname} +\def\setevalue #1{\expandafter\edef\csname#1\endcsname} +\def\setxvalue #1{\expandafter\xdef\csname#1\endcsname} +\def\getvalue #1{\csname#1\endcsname} +\def\letvalue #1{\expandafter\let\csname#1\endcsname} +\def\letgvalue #1{\global\expandafter\let\csname#1\endcsname} +\def\resetvalue #1{\expandafter\let\csname#1\endcsname\empty} +\def\undefinevalue#1{\expandafter\let\csname#1\endcsname\undefined} +\def\ignorevalue#1#2{\expandafter\let\csname#1\endcsname\empty} + +%D \macros +%D {globallet,glet} +%D +%D In \CONTEXT\ of May 2000 using \type {\globallet} +%D instead of the two tokens will save us some +%D $300\times4=1200$ bytes of format file on a 32~bit +%D system. So: + +\def\globallet{\global\let} \let\glet\globallet + +%D \macros +%D {donottest,unexpanded} +%D +%D When expansion of a macro gives problems, we can precede it +%D by \type{\donottest}. It seems that protection is one of the +%D burdens of developers of packages, so maybe that's why in +%D \ETEX\ protection is solved in a more robust way. +%D +%D Because we use thi smodule onl in \MKIV, we have removed the +%D old protection code. +%D +%D \starttyping +%D \unexpanded\def\somecommand{... ... ...} +%D \stoptyping + +\let \donottest \firstofoneargument % we need to weed +\let \honorunexpanded \empty % we need to weed +\let \forceunexpanded \empty % we need to weed +\let \resetunexpanded \empty % we need to weed + +\let \unexpanded \normalprotected + +%D \macros +%D {doifundefined,doifdefined, +%D doifundefinedelse,doifdefinedelse, +%D doifalldefinedelse} +%D +%D The standard way of testing if a macro is defined is +%D comparing its meaning with another undefined one, usually +%D \type{\undefined}. To garantee correct working of the next +%D set of macros, \type{\undefined} may never be defined! +%D +%D \starttyping +%D \doifundefined {string} {...} +%D \doifdefined {string} {...} +%D \doifundefinedelse {string} {then ...} {else ...} +%D \doifdefinedelse {string} {then ...} {else ...} +%D \doifalldefinedelse {commalist} {then ...} {else ...} +%D \stoptyping +%D +%D Every macroname that \TEX\ builds gets an entry in the hash +%D table, which is of limited size. It is expected that e-\TeX\ +%D will offer a less memory||consuming alternative. + +%D Although it will probably never be a big problem, it is good +%D to be aware of the difference between testing on a macro +%D name to be build by using \type{\csname} and +%D \type{\endcsname} and testing the \type{\name} directly. +%D +%D \starttyping +%D \expandafter\ifx\csname NameA\endcsname\relax ... \else ... \fi +%D +%D \ifundefined\NameB ... \else ... \fi +%D \stoptyping + +\def\ifundefined#1% ongelukkige naam .. obsolete + {\unless\ifcsname#1\endcsname} + +% \def\p!doifundefined#1% +% {\edef\p!defined{#1}% +% \unless\ifcsname\detokenize\@EA{\p!defined}\endcsname} + +% \def\doifundefinedelse#1% +% {\edef\p!defined{#1}% +% \ifcsname\detokenize\@EA{\p!defined}\endcsname +% \expandafter\secondoftwoarguments +% \else +% \expandafter\firstoftwoarguments +% \fi} + +% \def\doifdefinedelse#1% +% {\edef\p!defined{#1}% +% \ifcsname\detokenize\@EA{\p!defined}\endcsname +% \expandafter\firstoftwoarguments +% \else +% \expandafter\secondoftwoarguments +% \fi} + +% \def\doifundefined#1% +% {\edef\p!defined{#1}% +% \ifcsname\detokenize\@EA{\p!defined}\endcsname +% \expandafter\gobbleoneargument +% \else +% \expandafter\firstofoneargument +% \fi} + +% \def\doifdefined#1% +% {\edef\p!defined{#1}% +% \ifcsname\detokenize\@EA{\p!defined}\endcsname +% \expandafter\firstofoneargument +% \else +% \expandafter\gobbleoneargument +% \fi} + +\ifdefined\suppressifcsnameerror + + \suppressifcsnameerror\plusone + + \def\doifundefinedelse#1% + {\ifcsname#1\endcsname + \@EA\secondoftwoarguments\else\@EA\firstoftwoarguments + \fi} + + \def\doifdefinedelse#1% + {\ifcsname#1\endcsname + \@EA\firstoftwoarguments\else\@EA\secondoftwoarguments + \fi} + + \def\doifundefined#1% + {\ifcsname#1\endcsname + \@EA\gobbleoneargument\else\@EA\firstofoneargument + \fi} + + \def\doifdefined#1% + {\ifcsname#1\endcsname + \@EA\firstofoneargument\else\@EA\gobbleoneargument + \fi} + +\else + + \def\doifundefinedelse#1% + {\ifcsname\detokenize\@EA{\normalexpanded{#1}}\endcsname + \@EA\secondoftwoarguments\else\@EA\firstoftwoarguments + \fi} + + \def\doifdefinedelse#1% + {\ifcsname\detokenize\@EA{\normalexpanded{#1}}\endcsname + \@EA\firstoftwoarguments\else\@EA\secondoftwoarguments + \fi} + + \def\doifundefined#1% + {\ifcsname\detokenize\@EA{\normalexpanded{#1}}\endcsname + \@EA\gobbleoneargument\else\@EA\firstofoneargument + \fi} + + \def\doifdefined#1% + {\ifcsname\detokenize\@EA{\normalexpanded{#1}}\endcsname + \@EA\firstofoneargument\else\@EA\gobbleoneargument + \fi} + +\fi + +%D \macros +%D {letbeundefined} +%D +%D Testing for being undefined comes down to testing on \type +%D {\relax} when we use \type {\csname}, but when using \type +%D {\ifx}, we test on being \type {\undefined}! In \ETEX\ we +%D have \type {\ifcsname} and that way of testing on existance +%D is not the same as the one described here. Therefore we +%D introduce: + +\def\letbeundefined#1% potential stack buildup when used \global + {\expandafter\let\csname#1\endcsname\undefined} + +\def\localundefine#1% conditional + {\ifcsname#1\endcsname\expandafter\let\csname#1\endcsname\undefined\fi} + +\def\globalundefine#1% conditional + {\ifcsname#1\endcsname\expandafter\global\let\csname#1\endcsname\undefined\fi} + +%D Beware, being \type {\undefined} in \ETEX\ means that the macro +%D {\em is} defined! + +%D When we were developing the scientific units module, we +%D encountered different behavior in text and math mode, which +%D was due to this grouping subtilities. We therefore decided +%D to use \type{\begingroup} instead of \type{\bgroup}. + +\def\docheckonedefined#1% + {\ifcsname#1\endcsname\else + \donefalse + \expandafter\quitcommalist % added + \fi} + +\def\doifalldefinedelse#1% + {\begingroup + \donetrue \processcommalist[#1]\docheckonedefined + \ifdone + \endgroup\expandafter\firstoftwoarguments + \else + \endgroup\expandafter\secondoftwoarguments + \fi} + +%D \macros +%D {doif,doifelse,doifnot, +%D donottest} +%D +%D Programming in \TEX\ differs from programming in procedural +%D languages like \MODULA. This means that one --- well, let me +%D speek for myself --- tries to do the things in the well +%D known way. Therefore the next set of \type{\ifthenelse} +%D commands were between the first ones we needed. A few years +%D later, the opposite became true: when programming in +%D \MODULA, I sometimes miss handy things like grouping, +%D runtime redefinition, expansion etc. While \MODULA\ taught +%D me to structure, \TEX\ taught me to think recursive. +%D +%D \starttyping +%D \doif {string1} {string2} {...} +%D \doifnot {string1} {string2} {...} +%D \doifelse {string1} {string2} {then ...}{else ...} +%D \stoptyping +%D +%D When expansion gives problems, we can precede the +%D troublemaker with \type{\donottest}. + +\long\def\doif#1#2% + {\edef\!!stringa{#1}\edef\!!stringb{#2}% + \ifx\!!stringa\!!stringb + \expandafter\firstofoneargument + \else + \expandafter\gobbleoneargument + \fi} + +\long\def\doifnot#1#2% + {\edef\!!stringa{#1}\edef\!!stringb{#2}% + \ifx\!!stringa\!!stringb + \expandafter\gobbleoneargument + \else + \expandafter\firstofoneargument + \fi} + +\long\def\doifelse#1#2% + {\edef\!!stringa{#1}\edef\!!stringb{#2}% + \ifx\!!stringa\!!stringb + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +%D \macros +%D {doifempty,doifemptyelse,doifnotempty} +%D +%D We complete our set of conditionals with: +%D +%D \starttyping +%D \doifempty {string} {...} +%D \doifnotempty {string} {...} +%D \doifemptyelse {string} {then ...} {else ...} +%D \stoptyping +%D +%D This time, the string is not expanded. + +\long\def\doifemptyelse#1% + {\def\!!stringa{#1}% + \ifx\!!stringa\empty + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +\long\def\doifempty#1% + {\def\!!stringa{#1}% + \ifx\!!stringa\empty + \expandafter\firstofoneargument + \else + \expandafter\gobbleoneargument + \fi} + +\long\def\doifnotempty#1% + {\def\!!stringa{#1}% + \ifx\!!stringa\empty + \expandafter\gobbleoneargument + \else + \expandafter\firstofoneargument + \fi} + +%D \macros +%D {doifinset,doifnotinset,doifinsetelse} +%D +%D We can check if a string is present in a comma separated +%D set of strings. Depending on the result, some action is +%D taken. +%D +%D \starttyping +%D \doifinset {string} {string,...} {...} +%D \doifnotinset {string} {string,...} {...} +%D \doifinsetelse {string} {string,...} {then ...} {else ...} +%D \stoptyping + +% !0nop=\doifinsetelse{ccc}{,}{yes}{nop} +% !0nop=\doifinsetelse{ccc}{,,}{yes}{nop} +% !0nop=\doifinsetelse{ccc}{,,,}{yes}{nop} + +% !1nop=\doifinsetelse{}{}{yes}{nop} +% !2yes=\doifinsetelse{aaa}{bbb,ccc,ddd,aaa,eee}{yes}{nop} +% !3nop=\doifinsetelse{aaa}{bbb}{yes}{nop} +% !4yes=\doifinsetelse{aaa}{aaa}{yes}{nop} +% !5nop=\doifinsetelse{aaaa}{bbb,ccc,ddd,aaa,eee}{yes}{nop} +% !6nop=\doifinsetelse{}{}{yes}{nop} +% !7nop=\doifinsetelse{}{aaa}{yes}{nop} +% !8nop=\doifinsetelse{aaa}{}{yes}{nop} + +% !1=\doifinset{}{}{yes} +% !2yes=\doifinset{aaa}{bbb,ccc,ddd,aaa,eee}{yes} +% !3=\doifinset{aaa}{bbb}{yes} +% !4yes=\doifinset{aaa}{aaa}{yes} +% !5=\doifinset{}{}{yes} +% !6=\doifinset{aaa}{}{yes} + +% !1yes=\doifnotinset{}{}{yes} +% !2=\doifnotinset{aaa}{bbb,ccc,ddd,aaa,eee}{yes} +% !3yes=\doifnotinset{aaa}{bbb}{yes} +% !4=\doifnotinset{aaa}{aaa}{yes} +% !5yes=\doifnotinset{}{}{yes} +% !6yes=\doifnotinset{aaa}{}{yes} + +\def\rightoptionalbracket{]} + +\long\def\doquitifiteminsetelse#1],\relax{\firstoftwoarguments} +\long\def\doquitifiteminset #1],\relax{\firstofoneargument} +\long\def\doquitifitemnotinset #1],\relax{\gobbleoneargument} + +\long\def\redoifinsetelse{\expandafter\docheckifiteminsetelse\!!stringb,],\relax} +\long\def\redoifinset {\expandafter\docheckifiteminset \!!stringb,],\relax} +\long\def\redoifnotinset {\expandafter\docheckifitemnotinset \!!stringb,],\relax} + +\long\def\doifinsetelse#1% make this two step too + {\edef\!!stringa{#1}% + \ifx\!!stringa\empty + \expandafter\thirdofthreearguments + \else + \expandafter\dodoifinsetelse + \fi} +\long\def\dodoifinsetelse#1% + {\edef\!!stringb{#1}% + \ifx\!!stringb\empty + \expandafter\secondoftwoarguments + \else + \expandafter\redoifinsetelse + \fi} + +\long\def\doifinset#1% + {\edef\!!stringa{#1}% + \ifx\!!stringa\empty + \expandafter\gobbletwoarguments + \else + \expandafter\dodoifinset + \fi} +\long\def\dodoifinset#1% + {\edef\!!stringb{#1}% + \ifx\!!stringb\empty + \expandafter\gobbleoneargument + \else + \expandafter\redoifinset + \fi} + +\long\def\doifnotinset#1% + {\edef\!!stringa{#1}% + \ifx\!!stringa\empty + \expandafter\secondoftwoarguments + \else + \expandafter\dodoifnotinset + \fi} +\long\def\dodoifnotinset#1% + {\edef\!!stringb{#1}% + \ifx\!!stringb\empty + \expandafter\firstofoneargument + \else + \expandafter\redoifnotinset % ...]{true} + \fi} + +\def\docheckifiteminsetelse#1,#2% #2 eats up preceding space + {\edef\!!stringb{#1}% + \ifx\!!stringb\empty + \expandafter\docheckifiteminsetelse + \else + \expandafter\dodocheckifiteminsetelse + \fi#2} +\def\dodocheckifiteminsetelse + {\ifx\!!stringb\rightoptionalbracket + \expandafter\thirdofthreearguments + \else + \expandafter\dododocheckifiteminsetelse + \fi} +\def\dododocheckifiteminsetelse + {\ifx\!!stringa\!!stringb + \expandafter\doquitifiteminsetelse + \else + \expandafter\docheckifiteminsetelse + \fi} + +\def\docheckifiteminset#1,#2% #2 eats up preceding space + {\edef\!!stringb{#1}% + \ifx\!!stringb\empty + \expandafter\docheckifiteminset + \else + \expandafter\dodocheckifiteminset + \fi#2} +\def\dodocheckifiteminset + {\ifx\!!stringb\rightoptionalbracket + \expandafter\gobbletwoarguments + \else + \expandafter\dododocheckifiteminset + \fi} +\def\dododocheckifiteminset + {\ifx\!!stringa\!!stringb + \expandafter\doquitifiteminset + \else + \expandafter\docheckifiteminset + \fi} + +\def\docheckifitemnotinset#1,#2% #2 eats up preceding space + {\edef\!!stringb{#1}% + \ifx\!!stringb\empty + \expandafter\docheckifitemnotinset + \else + \expandafter\dodocheckifitemnotinset + \fi#2} +\def\dodocheckifitemnotinset + {\ifx\!!stringb\rightoptionalbracket + \expandafter\secondoftwoarguments + \else + \expandafter\dododocheckifitemnotinset + \fi} +\def\dododocheckifitemnotinset + {\ifx\!!stringa\!!stringb + \expandafter\doquitifitemnotinset + \else + \expandafter\docheckifitemnotinset + \fi} + +%D \macros +%D {doifcommon,doifnotcommon,doifcommonelse} +%D +%D Probably the most time consuming tests are those that test +%D for overlap in sets of strings. +%D +%D \starttyping +%D \doifcommon {string,...} {string,...} {...} +%D \doifnotcommon {string,...} {string,...} {...} +%D \doifcommonelse {string,...} {string,...} {then ...} {else ...} +%D \stoptyping + +% !1yes=\doifcommonelse{aaa,bbb,ccc}{aaa,bbb,ccc}{yes}{nop} +% !2nop=\doifcommonelse{aaa,bbb,ccc}{ddd,eee,fff}{yes}{nop} +% !3nop=\doifcommonelse{aaa}{ddd,eee,fff}{yes}{nop} +% !4yes=\doifcommonelse{aaa}{aaa}{yes}{nop} +% !5nop=\doifcommonelse{bbb}{aaa}{yes}{nop} +% !6nop=\doifcommonelse{}{aaa,bbb,ccc}{yes}{nop} +% !7nop=\doifcommonelse{aaa,bbb,ccc}{}{yes}{nop} +% !8nop=\doifcommonelse{}{}{yes}{nop} + +% !9nop=\doifcommonelse{,,}{,,}{yes}{nop} +% !9yes=\doifcommonelse{,a,}{,a,}{yes}{nop} +% !9yes=\doifcommonelse{,,a,}{,a,}{yes}{nop} +% !9yes=\doifcommonelse{,a,}{,,a,}{yes}{nop} +% !9yes=\doifcommonelse{,a,}{,,,a,}{yes}{nop} +% !9yes=\doifcommonelse{,,a,}{,,,a,}{yes}{nop} + +% \def\p!doifcommonelse#1#2#3#4% +% {\donefalse +% \def\p!docommoncheck##1{\doifinset{##1}{#4}\donetrue\ifdone\quitcommalist\fi}% +% \processcommalist[#3]\p!docommoncheck +% \ifdone\expandafter#1\else\expandafter#2\fi} +% +% \def\doifcommonelse +% {\p!doifcommonelse\firstoftwoarguments\secondoftwoarguments} +% +% \def\doifcommon +% {\p!doifcommonelse\firstofoneargument \gobbleoneargument} +% +% \def\doifnotcommon +% {\p!doifcommonelse\gobbleoneargument \firstofoneargument} + +\long\def\doquitifcommonelse#1],\relax#2],\relax{\firstoftwoarguments} + +\long\def\doquitifcommonelsenop{\secondoftwoarguments} + +\def\docheckifcommonelseone#1,#2% + {\edef\!!stringc{#1}% + \ifx\!!stringc\rightoptionalbracket + \expandafter\thirdofthreearguments + \else + \expandafter\p!docommoncheck + \fi#2} + +\def\docheckifcommonelsetwo#1,#2% we can do an empty #1 check too + {\edef\commalistelement{#1}% + \ifx\commalistelement\rightoptionalbracket + \expandafter\redocheckifcommonelseone + \else + \expandafter\dodocheckifcommonelsetwo + \fi#2} + +\def\dodocheckifcommonelsetwo + {\ifx\commalistelement\empty + \expandafter\docheckifcommonelsetwo + \else + \expandafter\dododocheckifcommonelsetwo + \fi} + +\def\dododocheckifcommonelsetwo + {\ifx\!!stringc\commalistelement + \expandafter\doquitifcommonelse + \else + \expandafter\docheckifcommonelsetwo + \fi} + +\def\redocheckifcommonelseone#1{\docheckifcommonelseone} + +\def\p!doifcommonelse#1#2#3#4% + {\edef\!!stringa{#3}% + \edef\!!stringb{#4}% + \ifx\!!stringa\empty + \expandafter\secondoftwoarguments + \else\ifx\!!stringb\empty + \expandafter\expandafter\expandafter\secondoftwoarguments + \else + \expandafter\expandafter\expandafter\pp!doifcommonelse + \fi\fi + #1#2} + +% \def\p!doifcommonelse#1#2#3% +% {\edef\!!stringa{#3}% +% \ifx\!!stringa\empty +% \expandafter\secondofthreearguments +% \else +% \expandafter\p!dodoifcommonelse +% \fi +% #1#2} % #4 + +% \def\p!dodoifcommonelse#1#2#3% +% {\edef\!!stringb{#3}% +% \ifx\!!stringb\empty +% \expandafter\secondoftwoarguments +% \else +% \expandafter\pp!doifcommonelse +% \fi#1#2} + +\def\pp!doifcommonelse + {\def\p!docommoncheck{\expandafter\docheckifcommonelsetwo\!!stringb,],\relax}% + \expandafter\docheckifcommonelseone\!!stringa,],\relax} + +\def\doifcommonelse{\p!doifcommonelse\firstoftwoarguments\secondoftwoarguments} +\def\doifcommon {\p!doifcommonelse\firstofoneargument \gobbleoneargument } +\def\doifnotcommon {\p!doifcommonelse\gobbleoneargument \firstofoneargument } + +%D \macros +%D {processcommalist,processcommacommand,quitcommalist, +%D processcommalistwithparameters} +%D +%D We've already seen some macros that take care of comma +%D separated lists. Such list can be processed with +%D +%D \starttyping +%D \processcommalist[string,string,...]\commando +%D \stoptyping +%D +%D The user supplied command \type{\commando} receives one +%D argument: the string. This command permits nesting and +%D spaces after commas are skipped. Empty sets are no problem. +%D +%D \startbuffer +%D \def\dosomething#1{(#1)} +%D +%D 1: \processcommalist [\hbox{$a,b,c,d,e,f$}] \dosomething \par +%D 2: \processcommalist [{a,b,c,d,e,f}] \dosomething \par +%D 3: \processcommalist [{a,b,c},d,e,f] \dosomething \par +%D 4: \processcommalist [a,b,{c,d,e},f] \dosomething \par +%D 5: \processcommalist [a{b,c},d,e,f] \dosomething \par +%D 6: \processcommalist [{a,b}c,d,e,f] \dosomething \par +%D 7: \processcommalist [] \dosomething \par +%D 8: \processcommalist [{[}] \dosomething \par +%D \stopbuffer +%D +%D \typebuffer +%D +%D Before we show the result, we present the macro's: + +\newcount\commalevel + +\def\dododoprocesscommaitem + {\csname\s!next\the\commalevel\endcsname} + +% \def\dodoprocesscommaitem +% {\ifx\nexttoken\blankspace +% \@EA\redoprocesscommaitem +% \else\ifx\nexttoken]% +% \@EAEAEA\gobbleoneargument +% \else +% \@EAEAEA\dododoprocesscommaitem +% \fi\fi} + +\def\dodoprocesscommaitem + {\ifx\nexttoken\blankspace + \@EA\redoprocesscommaitem + \else + \@EA\dodoprocesscommaitemindeed + \fi} +\def\dodoprocesscommaitemindeed + {\ifx\nexttoken]% + \@EA\gobbleoneargument + \else + \@EA\dododoprocesscommaitem + \fi} + +\def\doprocesscommaitem + {\futurelet\nexttoken\dodoprocesscommaitem} + +%D Empty arguments are not processed. Empty items (\type{,,}) +%D however are treated. We have to check for the special case +%D \type{[{a,b,c}]}. + +\def\processcommalist[% + {\futurelet\nexttoken\docheckcommaitem} + +\def\docheckcommaitem + {\ifx\nexttoken]% + \expandafter\gobblethreearguments + \else + \expandafter\doprocesscommalist + \fi + \relax} % this one preserved the next {} + +\def\doprocesscommalist#1]#2% + {\global\advance\commalevel \plusone + \long\expandafter\def\csname\s!next\the\commalevel\endcsname##1,% + {#2{##1}\doprocesscommaitem}% + \@EA\dodoprocesscommaitem\gobbleoneargument#1,]\relax + \global\advance\commalevel \minusone } + +%D One way of quitting a commalist halfway is: + +\def\quitcommalist + {\begingroup\let\doprocesscommaitem\doquitcommalist} + +\def\doquitcommalist#1]% + {\endgroup} + +\def\quitprevcommalist + {\begingroup\let\doprocesscommaitem\doquitprevcommalist} + +\def\doquitprevcommalist#1]% + {\let\doprocesscommaitem\doquitcommalist} + +%D The hack we used for checking the next character +%D \type {\doifnextcharelse} is also used here. + +\def\:{\redoprocesscommaitem} + +\expandafter\def\: {\futurelet\nexttoken\dodoprocesscommaitem} + +%D The previous examples lead to: +%D +%D \getbuffer + +%D When a list is saved in a macro, we can use a construction +%D like: +%D +%D \starttyping +%D \expandafter\processcommalist\expandafter[\list]\command +%D \stoptyping +%D +%D Such solutions suit most situations, but we wanted a bit +%D more. +%D +%D \starttyping +%D \processcommacommand[string,\stringset,string]\commando +%D \stoptyping +%D +%D where \type{\stringset} is a predefined set, like: +%D +%D \starttyping +%D \def\first{aap,noot,mies} +%D \def\second{laatste} +%D +%D \processcommacommand[\first]\message +%D \processcommacommand[\first,second,third]\message +%D \processcommacommand[\first,between,\second]\message +%D \stoptyping +%D +%D Commands that are part of the list are expanded, so the +%D use of this macro has its limits. + +% \def\processcommacommand[#1]% +% {\expanded{\processcommalist[#1]}} + +\def\processcommacommand[#1]% + {\expandafter\processcommalist\expandafter[\normalexpanded{#1}]} + +%D The argument to \type{\command} is not delimited. Because +%D we often use \type{[]} as delimiters, we also have: +%D +%D \starttyping +%D \processcommalistwithparameters[string,string,...]\command +%D \stoptyping +%D +%D where \type{\command} looks like: +%D +%D \starttyping +%D \def\command[#1]{... #1 ...} +%D \stoptyping + +\def\processcommalistwithparameters[#1]#2% + {\def\docommand##1{#2[##1]}% + \processcommalist[#1]\docommand} + +%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}% + \normalexpanded{\noexpand\processcommacommand[#1]}\currentcommalistcommand} + +%D \macros +%D {processaction, +%D processfirstactioninset, +%D processallactionsinset} +%D +%D \CONTEXT\ makes extensive use of a sort of case or switch +%D command. Depending of the presence of one or more provided +%D items, some actions is taken. These macros can be nested +%D without problems. +%D +%D \starttyping +%D \processaction [x] [a=>\a,b=>\b,c=>\c] +%D \processfirstactioninset [x,y,z] [a=>\a,b=>\b,c=>\c] +%D \processallactionsinset [x,y,z] [a=>\a,b=>\b,c=>\c] +%D \stoptyping +%D +%D We can supply both a \type{default} action and an action +%D to be undertaken when an \type{unknown} value is met: +%D +%D \starttyping +%D \processallactionsinset +%D [x,y,z] +%D [ a=>\a, +%D b=>\b, +%D c=>\c, +%D default=>\default, +%D unknown=>\unknown{... \commalistelement ...}] +%D \stoptyping +%D +%D When \type{#1} is empty, this macro scans list \type{#2} for +%D the keyword \type{default} and executed the related action +%D if present. When \type{#1} is non empty and not in the list, +%D the action related to \type{unknown} is executed. Both +%D keywords must be at the end of list \type{#2}. Afterwards, +%D the actually found keyword is available in +%D \type{\commalistelement}. An advanced example of the use of +%D this macro can be found in \PPCHTEX, where we completely +%D rely on \TEX\ for interpreting user supplied keywords like +%D \type{SB}, \type{SB1..6}, \type{SB125} etc. + +\newcount\processlevel + +\def\p!compareprocessactionA[#1=>#2][#3]% + {\edef\!!stringb{#1}% + \ifx\!!stringb\s!default + \let\commalistelement\empty + #2% + \fi} + +% met \quitcommalist tot meer dan 25\% sneller + +\def\p!compareprocessactionB[#1=>#2][#3]% + {\expandedaction\!!stringb{#1}% + \ifx\!!stringa\!!stringb + \def\commalistelement{#3}% + #2% + \expandafter\quitcommalist + \else + \edef\!!stringb{#1}% + \ifx\!!stringb\s!unknown + \def\commalistelement{#3}% beware of loops + #2% + \fi + \fi} + +\def\processaction[#1]#2[% + {\expandedaction\!!stringa{#1}% + \ifx\!!stringa\empty + \let\p!compareprocessaction\p!compareprocessactionA + \else + \let\p!compareprocessaction\p!compareprocessactionB + \fi + \def\p!doprocessaction##1% + {\p!compareprocessaction[##1][#1]}% + \processnextcommalist\relax\expandactions\p!doprocessaction[} + +\def\p!compareprocessactionC[#1=>#2][#3]% + {\expandedaction\!!stringa{#1}% + \expandedaction\!!stringb{#3}% + \ifx\!!stringa\!!stringb + \def\commalistelement{#3}% + #2% + \expandafter\quitprevcommalist + \else + \edef\!!stringa{#1}% + \ifx\!!stringa\s!unknown + \def\commalistelement{#3}% + #2% + \fi + \fi} + +\def\processfirstactioninset[#1]% + {\expandedaction\!!stringa{#1}% + \ifx\!!stringa\empty + \expandafter\processaction + \else + \expandafter\processfirstactionsinsetindeed + \fi + [#1]} + +\def\processfirstactionsinsetindeed[#1]#2[#3]% + {\def\p!doprocessaction##1% + {\def\p!dodoprocessaction####1% + {\p!compareprocessactionC[####1][##1]}% + \processcommalist[#3]\p!dodoprocessaction}% + \processcommalist[#1]\p!doprocessaction + \expandactions} + +\def\p!compareprocessactionD[#1=>#2][#3]% + {\expandedaction\!!stringa{#1}% + \expandedaction\!!stringb{#3}% + \ifx\!!stringa\!!stringb + \def\commalistelement{#3}% + #2% + \expandafter\quitcommalist + \else + \edef\!!stringa{#1}% + \ifx\!!stringa\s!unknown + \def\commalistelement{#3}% + #2% + \fi + \fi} + +\def\doprocessallactionsinset + {\csname\s!do\the\processlevel\endcsname} + +\def\processallactionsinset[#1]% + {\expandedaction\!!stringa{#1}% + \ifx\!!stringa\empty + \expandafter\processaction + \else + \expandafter\processallactionsinsetindeed + \fi + [#1]} + +\def\processallactionsinsetindeed[#1]#2[#3]% + {\advance\processlevel \plusone + \expandafter\def\csname\s!do\the\processlevel\endcsname##1% + {\def\p!dodoprocessaction####1% + {\p!compareprocessactionD[####1][##1]}% + \processcommalist[#3]\p!dodoprocessaction}% + \processcommalist[#1]\doprocessallactionsinset + \advance\processlevel \minusone + \expandactions} + +%D These macros use: + +\def\processnextcommalist#1#2#3[#4#5]% + {#1% + \let\nexttoken#4% + \global\advance\commalevel \plusone + \long\expandafter\def\csname\s!next\the\commalevel\endcsname##1,% + {#3{##1}\doprocesscommaitem}% + \dodoprocesscommaitem#4#5,]\relax + \global\advance\commalevel \minusone + #2} + +%D \macros +%D {unexpandedprocessaction, +%D unexpandedprocessfirstactioninset, +%D unexpandedprocessallactionsinset} +%D +%D Now what are those expansion commands doing there. Well, +%D sometimes we want to compare actions that may consist off +%D commands (i.e. are no constants). In such occasions we can +%D use the a bit slower alternatives: + +\def\unexpandedprocessfirstactioninset{\dontexpandactions\processfirstactioninset} +\def\unexpandedprocessaction {\dontexpandactions\processaction} +\def\unexpandedprocessallactionsinset {\dontexpandactions\processallactionsinset} + +%D By default we expand actions: + +\def\expandactions{\let\expandedaction\edef} \expandactions + +%D But when needed we convert the strings to meaningful +%D sequences of characters. + +\def\unexpandedaction#1>{} + +\def\noexpandedaction#1#2% + {\def\@@convertedargument{#2}% + \@EA\edef\@EA#1\@EA{\@EA\unexpandedaction\meaning\@@convertedargument}} + +\def\dontexpandactions + {\let\expandedaction\noexpandedaction} + +%D \macros +%D {getfirstcharacter, firstcharacter, remainingcharacters, doiffirstcharacter} +%D +%D Sometimes the action to be undertaken depends on the +%D next character. This macro get this character and puts it in +%D \type{\firstcharacter}. +%D +%D \starttyping +%D \getfirstcharacter {string} +%D \stoptyping +%D +%D A two step expansion is used to prevent problems with +%D complicated arguments, for instance arguments that +%D consist of two or more expandable tokens. + +\def\dogetfirstcharacter#1#2\relax + {\def\firstcharacter{#1}% + \def\remainingcharacters{#2}} + +\def\getfirstcharacter#1% + {\edef\!!stringa{#1}% + \expandafter\dogetfirstcharacter\!!stringa\relax} + +\def\doiffirstcharelse#1#2% char string +% kort (maar onleesbaar) +% {\expanded{\dogetfirstcharacter#2}\\\doifelse{#1}\firstcharacter} +% korter (en begrijpelijk)) + {\getfirstcharacter{#2}\doifelse{#1}\firstcharacter} +% snel (maar zelden gebruikt, dus niet zo belangrijk) +% {\getfirstcharacter{#2}% +% \edef\!!stringa{#1}% +% \ifx\!!stringa\firstcharacter +% \expandafter\firstoftwoarguments +% \else +% \expandafter\secondoftwoarguments +% \fi} + +%D \macros +%D {doifinstringelse, doifincsnameelse} +%D +%D We can check for the presence of a substring in a given +%D sequence of characters. +%D +%D \starttyping +%D \doifinsetelse {substring} {string} {then ...} {else ...} +%D \stoptyping + +\long\def\doifinstringelse#1% + {\edef\@@@instring{#1}% expand #1 here + \ifx\@@@instring\empty + \@EA\thirdofthreearguments + \else + \@EA\dodoifinstringelse + \fi} + +\long\def\dodoifinstringelse#1% + {\p!doifinstringelse\@@@instring{#1}% + \@EA\firstoftwoarguments + \else + \@EA\secondoftwoarguments + \fi} + +\long\def\doifinstring#1%% + {\edef\@@@instring{#1}% expand #1 here + \ifx\@@@instring\empty + \@EA\gobbletwoarguments + \else + \@EA\dodoifinstring + \fi} + +\long\def\dodoifinstring#1% + {\p!doifinstringelse\@@@instring{#1}% + \@EA\firstofoneargument + \else + \@EA\gobbleoneargument + \fi} + +\long\def\doifnotinstring#1%% + {\edef\@@@instring{#1}% expand #1 here + \ifx\@@@instring\empty + \@EA\gobbletwoarguments + \else + \@EA\dodoifnotinstring + \fi} + +\long\def\dodoifnotinstring#1% + {\p!doifinstringelse\@@@instring{#1}% + \@EA\gobbleoneargument + \else + \@EA\firstofoneargument + \fi} + +% replaces prev + +% \long\def\p!doifinstringelse#1#2% ##2 can be {abc} +% {\long\@EA\def\@EA\pp!doifinstringelse\@EA##\@EA1#1##2##3\war{\unless\if##2@}% expand #1 here +% \expanded{\pp!doifinstringelse#2#1}@@\war} % expand #2 here + +\long\def\p!doifinstringelse#1#2% ##2 can be {abc} + {\long\@EA\def\@EA\pp!doifinstringelse\@EA##\@EA1#1##2##3\war{\unless\if##2@}% expand #1 here + \expandafter\pp!doifinstringelse\normalexpanded{#2#1}@@\war} % expand #2 here + +% faster but at some costs +% +% \def\setp!doifinstringelse#1#2% ##2 can be {abc} +% {\long\expandafter\gdef\csname @diie:#1\@EA\endcsname\@EA##\@EA1#1##2##3\war{\unless\if##2@}}% expand #1 here +% +% \long\def\p!doifinstringelse#1#2% ##2 can be {abc} +% {\ifcsname @diie:#1\endcsname \else +% \setp!doifinstringelse{#1}{#2}% +% \fi +% \csname @diie:#1\expandafter\endcsname\normalexpanded{#2#1}@@\war} % expand #2 here + +%D The next alternative proved to be upto twice as fast on +%D tasks like checking reserved words in pretty verbatim +%D typesetting! This is mainly due to the fact that passing +%D (expanded) strings is much slower that passing a macro. +%D +%D \starttyping +%D \doifincsnameelse {substring} {\string} {then ...} {else ...} +%D \stoptyping +%D +%D Where \type{\doifinstringelse} does as much expansion as +%D possible, the latter alternative does minimal (one level) +%D expansion. + +\long\def\p!doifincsnameelse#1#2% + {\long\def\pp!doifincsnameelse##1#1##2##3\war + {\unless\if##2@}% + \@EA\pp!doifincsnameelse#2#1@@\war} + +\long\def\doifincsnameelse#1#2% % #3#4% + {\edef\@@@instring{#1}% + \@EA\p!doifincsnameelse\@EA{\@@@instring}{#2}% % #3\else#4\fi} + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +%D \macros +%D {doifnumberelse} +%D +%D The next macro executes a command depending of the outcome +%D of a test on numerals. This is probably one of the fastest +%D test possible, exept from a less robust 10||step +%D \type{\if}||ladder or some tricky \type{\lcode} checking. +%D +%D \starttyping +%D \doifnumberelse {string} {then ...} {else ...} +%D \stoptyping +%D +%D The macro accepts \type{123}, \type{abc}, \type{{}}, +%D \type{\getal} and \type{\the\count...}. This macro is a +%D rather dirty one. + +\long\def\doifnumberelse#1% does not accept counters + {\ifcase0\ifcase1#1\or\or\or\or\or\or\or\or\or\else1\fi\space + \expandafter\secondoftwoarguments + \else + \expandafter\firstoftwoarguments + \fi} + +%D \macros +%D {makerawcommalist, +%D rawdoinsetelse, +%D rawprocesscommalist, +%D rawprocessaction} +%D +%D Some of the commands mentioned earlier are effective but +%D slow. When one is desperately in need of faster alternatives +%D and when the conditions are predictable safe, the \type{\raw} +%D alternatives come into focus. A major drawback is that +%D they do not take \type{\c!constants} into account, simply +%D because no expansion is done. This is no problem with +%D \type{\rawprocesscommalist}, because this macro does not +%D compare anything. Expandable macros are permitted as search +%D string. +%D +%D \starttyping +%D \makerawcommalist[string,string,...]\stringlist +%D \rawdoifinsetelse{string}{string,...}{...}{...} +%D \rawprocesscommalist[string,string,...]\commando +%D \rawprocessaction[x][a=>\a,b=>\b,c=>\c] +%D \stoptyping +%D +%D Spaces embedded in the list, for instance after commas, +%D spoil the search process. The gain in speed depends on the +%D length of the argument (the longer the argument, the less +%D we gain). + +\def\makerawcommalist[#1]#2% use \processnext ... here + {\def\domakerawcommalist##1% we don't expand ##1 + {\ifx#2\empty + \def#2{##1}% + \else + \@EA\def\@EA#2\@EA{#2,##1}% + \fi}% + \let#2\empty + \processcommalist[#1]\domakerawcommalist} + +\def\rawprocesscommaitem#1,#2% #2 eats up preceding space + {\if]#1\else + \csname\s!next\the\commalevel\endcsname{#1}% + \expandafter\rawprocesscommaitem + \fi#2} + +\unexpanded\def\rawprocesscommalist[#1]#2% accepteert ook [\cs] + {\global\advance\commalevel \plusone + \expandafter\let\csname\s!next\the\commalevel\endcsname#2% + \expandafter\rawprocesscommaitem#1,],% \relax + \global\advance\commalevel \minusone } + +\def\rawprocesscommacommand[#1]% not really needed + {\normalexpanded{\rawprocesscommalist[#1]}} + +% \def\rawdoifinsetelse#1#2{\doifinstringelse{,#1,}{,#2,}} +% \def\rawdoifinset #1#2{\doifinstring {,#1,}{,#2,}} + +\def\@@rawempty{,,} + +\long\def\rawdoifinsetelse#1% + {\edef\@@@instring{,#1,}% expand #1 here + \ifx\@@@instring\@@rawempty + \@EA\thirdofthreearguments + \else + \@EA\rawdodoifinsetelse + \fi} + +\long\def\rawdodoifinsetelse#1% + {\p!doifinstringelse\@@@instring{,#1,}% + \@EA\firstoftwoarguments + \else + \@EA\secondoftwoarguments + \fi} + +\long\def\rawdoifinset#1% + {\edef\@@@instring{,#1,}% expand #1 here + \ifx\@@@instring\@@rawempty + \@EA\gobbletwoarguments + \else + \@EA\rawdodoifinset + \fi} + +\long\def\rawdodoifinset#1%% + {\p!doifinstringelse\@@@instring{,#1,}% + \@EA\firstofoneargument + \else + \@EA\gobbleoneargument + \fi} + +%D Some more raw material: + +\def\p!rawprocessaction[#1][#2]% + {\def\pp!rawprocessaction##1,#1=>##2,##3\war% + {\if##3@\else + \def\!!processaction{##2}% + \fi}% + \pp!rawprocessaction,#2,#1=>,@\war} + +\def\rawprocessaction[#1]#2[#3]% + {\edef\!!stringa{#1}% + \edef\!!stringb{undefined}% better \!!undefined + \let\!!processaction\!!stringb + \ifx\!!stringa\empty + \@EA\p!rawprocessaction\@EA[\s!default][#3]% + \else + \expandafter\p!rawprocessaction\expandafter[\!!stringa][#3]% + \ifx\!!processaction\!!stringb + \@EA\p!rawprocessaction\@EA[\s!unknown][#3]% + \fi + \fi + \ifx\!!processaction\!!stringb + \else + \!!processaction + \fi} + +%D When we process the list \type{a,b,c,d,e}, the raw routine +%D takes over 30\% less time, when we feed $20+$ character +%D strings we gain about 20\%. Alternatives which use +%D \type{\futurelet} perform worse. Part of the speedup is +%D due to the \type{\let} and \type{\expandafter} in the test. + +%D \macros +%D {dosetvalue,dosetevalue,dosetgvalue,docopyvalue,doresetvalue, +%D dogetvalue} +%D +%D When we are going to do assignments, we have to take +%D multi||linguality into account. For the moment we keep +%D things simple and single||lingual. +%D +%D \starttyping +%D \dosetvalue {label} {variable} {value} +%D \dosetevalue {label} {variable} {value} +%D \dosetgvalue {label} {variable} {value} +%D \docopyvalue {to label} {from label} {variable} +%D \doresetvalue {label} {variable} +%D \stoptyping +%D +%D These macros are in fact auxiliary ones and are not meant +%D for use outside the assignment macros. + +\def\dosetvalue#1#2% #3 + {\@EA\def\csname#1#2\endcsname} % {#3}} + +\def\dosetevalue#1#2% #3 + {\@EA\edef\csname#1#2\endcsname} % {#3}} + +\def\dosetgvalue#1#2% #3 + {\@EA\gdef\csname#1#2\endcsname} % {#3}} + +\def\doresetvalue#1#2% + {\@EA\let\csname#1#2\endcsname\empty} + +\def\doignorevalue#1#2#3% + {\@EA\let\csname#1#2\endcsname\empty} + +\def\docopyvalue#1#2#3% + {\@EA\def\csname#1#3\endcsname{\csname#2#3\endcsname}} + +%D \macros +%D {doassign,undoassign,doassignempty} +%D +%D Assignments are the backbone of \CONTEXT. Abhorred by the +%D concept of style file hacking, we took a considerable effort +%D in building a parameterized system. Unfortunately there is a +%D price to pay in terms of speed. Compared to other packages +%D and taking the functionality of \CONTEXT\ into account, the +%D total size of the format file is still very acceptable. Now +%D how are these assignments done. +%D +%D Assignments can be realized with: +%D +%D \starttyping +%D \doassign[label][variable=value] +%D \undoassign[label][variable=value] +%D \stoptyping +%D +%D and: +%D +%D \starttyping +%D \doassignempty[label][variable=value] +%D \stoptyping +%D +%D Assignments like \type{\doassign} are compatible with: +%D +%D \starttyping +%D \def\labelvariable{value} +%D \stoptyping +%D +%D We do check for the presence of an \type{=} and loudly +%D complain of it's missed. We will redefine this macro later +%D on, when a more advanced message mechanism is implemented. + +\newif\iferrorisfatal + +\def\waitonfatalerror + {\iferrorisfatal\wait\fi} + +\def\showassignerror#1#2% + {\writestatus{setup}{missing or ungrouped '=' after '#1' in line #2}% + \waitonfatalerror} + +\def\doassignempty[#1][#2=#3]% + {\ifcsname#1#2\endcsname\else\dosetvalue{#1}{#2}{#3}\fi} + +%D \macros +%D {getparameters,geteparameters,getgparameters, +%D forgetparameters} +%D +%D Using the assignment commands directly is not our +%D ideal of user friendly interfacing, so we take some further +%D steps. +%D +%D \starttyping +%D \getparameters [label] [...=...,...=...] +%D \forgetparameters [label] [...=...,...=...] +%D \stoptyping +%D +%D Again, the label identifies the category a variable +%D belongs to. The second argument can be a comma separated +%D list of assignments. +%D +%D \starttyping +%D \getparameters +%D [demo] +%D [alfa=1, +%D beta=2] +%D \stoptyping +%D +%D is equivalent to +%D +%D \starttyping +%D \def\demoalfa{1} +%D \def\demobeta{2} +%D \stoptyping +%D +%D +%D In the pre||multi||lingual stadium \CONTEXT\ took the next +%D approach. With +%D +%D \starttyping +%D \def\??demo {@@demo} +%D \def\!!alfa {alfa} +%D \def\!!beta {beta} +%D \stoptyping +%D +%D calling +%D +%D \starttyping +%D \getparameters +%D [\??demo] +%D [\!!alfa=1, +%D \!!beta=2] +%D \stoptyping +%D +%D lead to: +%D +%D \starttyping +%D \def\@@demoalfa{1} +%D \def\@@demobeta{2} +%D \stoptyping +%D +%D Because we want to be able to distinguish the \type{!!} +%D pre||tagged user supplied variables from internal +%D counterparts, we will introduce a slightly different tag in +%D the multi||lingual modules. There we will use \type{c!} or +%D \type{v!}, depending on the context. +%D +%D By calling \type{\p!doassign} directly, we save ourselves +%D some argument passing and gain some speed. Whatever +%D optimizations we do, this command will always be one of the +%D bigger bottlenecks. +%D +%D The alternative \type{\geteparameters} --- it's funny to +%D see that this alternative saw the light so lately --- can be +%D used to do expanded assigments. + +\let\currentvalue\empty + +\def\getparameters {\dogetparameters\dosetvalue} +\def\geteparameters {\dogetparameters\dosetevalue} +\def\getgparameters {\dogetparameters\dosetgvalue} +\def\forgetparameters{\dogetparameters\doignorevalue} + +\let\getexpandedparameters=\geteparameters + +\def\dogetparameters#1[#2]#3[#4% + {\if\noexpand#4]% + \expandafter\gobbleoneargument + \else + \let\setsomevalue#1% + \def\p!dogetparameter{\p!doassign#2}% + \expandafter\xdogetparameters + \fi#4} + +\def\xdogetparameters#1]% + {\xprocesscommaitem#1,],\@relax@} + +\long\def\xprocesscommaitem#1,#2% #2 takes space before , + {\if,#1,% dirty trick for testing #1=empty + \@EA\xprocesscommaitem + \else\if]#1% + \@EAEAEA\gobbleoneargument + \else + \p!dogetparameter\@relax@#1==\empty\@relax@ + \@EAEAEA\xprocesscommaitem + \fi\fi#2} + +\def\xshowassignerror#1#2#3% + {\showassignerror{#2}{\the\inputlineno\space(#1)}} + +\def\p!n!doassign#1\@relax@#2=#3=#4#5\@relax@ + {\ifx\empty#2\empty + \@EA\xshowassignerror + \else\ifx#4\empty + \@EAEAEA\xshowassignerror + \else + \@EAEAEA\setsomevalue + \fi\fi + {#1}{#2}{#3}} + +\def\p!e!doassign#1\@relax@#2=#3=#4#5\@relax@ + {\ifx\empty#2\empty + \@EA\xshowassignerror + \else\ifx#4\empty + \@EAEAEA\xshowassignerror + \else + \ifcsname#1#2\endcsname + \@EA\let\@EA\currentvalue\csname#1#2\endcsname + \else + \let\currentvalue\empty + \fi + \@EAEAEA\setsomevalue + \fi\fi + {#1}{#2}{#3}} + +\let\p!doassign\p!n!doassign + +% \def\doassign [#1][#2]{\p!doassign\dosetvalue #1\@relax@#2==\empty\@relax@} +% \def\doeassign [#1][#2]{\p!doassign\dosetevalue #1\@relax@#2==\empty\@relax@} +% \def\undoassign[#1][#2]{\p!doassign\doresetvalue#1\@relax@#2==\empty\@relax@} + +\def\doassign [#1][#2]{\let\setsomevalue\dosetvalue \p!doassign#1\@relax@#2==\empty\@relax@} +\def\doeassign [#1][#2]{\let\setsomevalue\dosetevalue \p!doassign#1\@relax@#2==\empty\@relax@} +\def\undoassign[#1][#2]{\let\setsomevalue\doresetvalue\p!doassign#1\@relax@#2==\empty\@relax@} + +%D \macros +%D {processassignmentlist,processassignmentcommand, +%D startprocessassignmentlist,startprocessassignmentcommand} +%D +%D For Wolfgang: +%D +%D \starttyping +%D \def\showpair#1#2{key:#1, value:#2\par} +%D \processassignmentlist[a=1,b=2]\showpair +%D \stoptyping +%D +%D We can optimize this one if needed but it's not a core macro so hardly +%D worth the trouble and tokens. + +\def\processassignmentlist[#1]#2% #2 == \command{key}{value] + {\def\doprocessassignmententry##1{#2}% {##2}{##3} % namespace is ignored + \dogetparameters\doprocessassignmententry[][#1]} + +\def\processassignmentcommand[#1]% + {\normalexpanded{\noexpand\processassignmentlist[#1]}} + +\long\def\startprocessassignmentlist[#1]#2\stopprocessassignmentlist + {\long\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}% + \processassignmentlist[#1]\currentassignmentlistcommand} + +\long\def\startprocessassignmentcommand[#1]#2\stopprocessassignmentcommand + {\long\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}% + \normalexpanded{\noexpand\processassignmentlist[#1]}\currentassignmentlistcommand} + +%D \macros{currentvalue} +%D +%D Just in case a \type{\getparameter} argument itself ends up +%D inside a \type{\write} or other expandable location, our +%D new macro needs a default value. +%D +%D \starttyping +%D \getparameters[xxx][aaa=bbb]\par +%D \getparameters[xxx][=bbb]\par +%D \getparameters[xxx][aaa=]\par +%D \getparameters[xxx][=]\par +%D \getparameters[xxx][aaa]\par +%D \stoptyping + +%D \macros {expandparameters} +%D +%D Example usage: +%D +%D \startbuffer +%D \getparameters[taco][name=taco] +%D \convertcommand\taconame\to\ascii \ascii +%D \expandparameters \getparameters[taco][name=\currentvalue\space hoekwater] +%D \convertcommand\taconame\to\ascii \ascii +%D \getparameters[taco][name=\currentvalue\space hoekwater] +%D \convertcommand\taconame\to\ascii \ascii +%D \stopbuffer +%D +%D \typebuffer +%D \startlines +%D \getbuffer +%D \stoplines + +%D Here we hook in the code (beware, this is the optimized get **): + +\def\xdoget@n@parameters#1]% + {\xprocesscommaitem#1,],\@relax@} + +\def\xdoget@e@parameters#1]% + {\let\dosetnvalue\dosetvalue + \let\dosetvalue\dosetevalue + \let\p!doassign\p!e!doassign + \xprocesscommaitem#1,],\@relax@ + \let\p!doassign\p!n!doassign + \let\dosetvalue\dosetnvalue + \let\xdogetparameters\xdoget@n@parameters + \let\currentvalue\empty} + +\let\xdogetparameters\xdoget@n@parameters % ** + +\def\expandparameters{\let\xdogetparameters\xdoget@e@parameters} + +%D \macros +%D {getemptyparameters} +%D +%D Sometimes we explicitly want variables to default to an +%D empty string, so we welcome: +%D +%D \starttyping +%D \getemptyparameters [label] [...=...,...=...] +%D \stoptyping + +\def\getemptyparameters[#1]#2[#3]% + {\def\p!dogetemptyparameter##1{\doassignempty[#1][##1]}% + \processcommalist[#3]\p!dogetemptyparameter} + +%D \macros +%D {copyparameters} +%D +%D Some \CONTEXT\ commands take their default setups from +%D others. All commands that are able to provide backgounds +%D or rules around some content, for instance default to the +%D standard command for ruled boxes. Is situations like this +%D we can use: +%D +%D \starttyping +%D \copyparameters [to-label] [from-label] [name1,name2,...] +%D \stoptyping +%D +%D For instance +%D +%D \starttyping +%D \copyparameters +%D [internal][external] +%D [alfa,beta] +%D \stoptyping +%D +%D Leads to: +%D +%D \starttyping +%D \def\internalalfa {\externalalfa} +%D \def\internalbeta {\externalbeta} +%D \stoptyping +%D +%D By using \type{\docopyvalue} we've prepared this command +%D for use in a multi||lingual environment. + +\def\copyparameters[#1]#2[#3]#4[#5]% + {\doifnot{#1}{#3} + {\def\docopyparameter{\docopyvalue{#1}{#3}}% ##1 + \processcommalist[#5]\docopyparameter}} + +%D \macros +%D {ifparameters,checkparameters} +%D +%D A slightly different one is \type{\checkparameters}, which +%D also checks on the presence of a~\type{=}. +%D +%D The boolean \type{\ifparameters} can be used afterwards. +%D Combining both in one \type{\if}||macro would lead to +%D problems with nested \type{\if}'s. +%D +%D \starttyping +%D \checkparameters[argument] +%D \stoptyping + +\newif\ifparameters + +\def\p!checkparameters#1=#2#3\war% + {\if#2@\parametersfalse\else\parameterstrue\fi} + +\def\checkparameters[#1]% + {\p!checkparameters#1=@@\war} + +%D \macros +%D {getfromcommalist,getfromcommacommand, +%D commalistelement, +%D getcommalistsize,getcommacommandsize} +%D +%D It's possible to get an element from a commalist or a +%D command representing a commalist. +%D +%D \starttyping +%D \getfromcommalist [string] [n] +%D \getfromcommacommand [string,\strings,string,...] [n] +%D \stoptyping +%D +%D The difference betwee the two of them is the same as the +%D difference between \type{\processcomma...}. The found string +%D is stored in \type{\commalistelement}. +%D +%D We can calculate the size of a comma separated list by +%D using: +%D +%D \starttyping +%D \getcommalistsize [string,string,...] +%D \getcommacommandsize [string,\strings,string,...] +%D \stoptyping +%D +%D Afterwards, the length is available in the macro +%D \type{\commalistsize} (not a \COUNTER). + +\newcount\commalistcounter + +\def\commalistsize{0} + +\def\p!dogetcommalistsize#1% + {\advance\commalistcounter\plusone} + +\def\getcommalistsize#1]% don't loose [{#1}] + {\commalistcounter\zerocount + \processcommalist#1]\p!dogetcommalistsize % was [{#1}] + \edef\commalistsize{\the\commalistcounter}} + +\def\getcommacommandsize[#1]% + {\edef\commacommand{#1}% + \scratchtoks\expandafter{\expandafter[\commacommand]}% + \expandafter\getcommalistsize\the\scratchtoks } + +% to be tested first +% +% \def\getcommacommandsize[#1]% +% {\expanded{\getcommalistsize[#1]}} + +% \def\p!dogetfromcommalist#1% +% {\advance\commalistcounter \minusone +% \ifcase\commalistcounter +% \def\commalistelement{#1}% +% \begingroup\def\doprocesscommaitem##1]{\endgroup}% +% \fi} + +\def\p!dogetfromcommalist#1% + {\advance\commalistcounter \minusone + \ifcase\commalistcounter + \def\commalistelement{#1}% + \expandafter\quitcommalist + \fi} + +\unexpanded\def\getfromcommalist[#1]#2[#3]% + {\let\commalistelement\empty + \commalistcounter#3\relax + \processcommalist[#1]\p!dogetfromcommalist} + +\unexpanded\def\getfromcommacommand[#1]% + {\normalexpanded{\getfromcommalist[#1]}} + +%D Watertight (and efficient) solutions are hard to find, due +%D to the handling of braces during parameters passing and +%D scanning. Nevertheless: +%D +%D \startbuffer +%D \def\dosomething#1{(#1=\commalistsize) } +%D +%D \getcommalistsize [\hbox{$a,b,c,d,e,f$}] \dosomething 1 +%D \getcommalistsize [{a,b,c,d,e,f}] \dosomething 1 +%D \getcommalistsize [{a,b,c},d,e,f] \dosomething 4 +%D \getcommalistsize [a,b,{c,d,e},f] \dosomething 4 +%D \getcommalistsize [a{b,c},d,e,f] \dosomething 4 +%D \getcommalistsize [{a,b}c,d,e,f] \dosomething 4 +%D \getcommalistsize [] \dosomething 0 +%D \getcommalistsize [{[}] \dosomething 1 +%D \stopbuffer +%D +%D \typebuffer +%D +%D reports: +%D +%D \getbuffer + +%D \macros +%D {dogetcommalistelement,dogetcommacommandelement} +%D +%D For low level (fast) purposes, we can also use the next +%D alternative, which can handle 8~elements at most. +%D +%D \starttyping +%D \dogetcommalistelement1\from a,b,c\to\commalistelement +%D \stoptyping + +\def\dodogetcommalistelement#1\from#2,#3,#4,#5,#6,#7,#8\to#9% + {\edef#9{\ifcase#1\relax\or#2\or#3\or#4\or#5\or#6\or#7\or#8\fi}} + +\def\dogetcommalistelement#1\from#2\to% + {\dodogetcommalistelement#1\from#2,,,,,,\to} + +% check sources + +\def\dogetcommacommandelement#1\from#2\to% + {\@EA\dodogetcommalistelement\@EA#1\@EA\from#2,,,,,,\to} + +%D \macros +%D {dosingleargument,dodoubleargument,dotripleargument, +%D doquadrupleargument,doquintupleargument,dosixtupleargument, +%D doseventupleargument} +%D +%D When working with delimited arguments, spaces and +%D lineendings can interfere. The next set of macros uses +%D \TEX' internal scanner for grabbing everything between +%D arguments. Forgive me the funny names. +%D +%D \starttyping +%D \dosingleargument\commando = \commando[#1] +%D \dodoubleargument\commando = \commando[#1][#2] +%D \dotripleargument\commando = \commando[#1][#2][#3] +%D \doquadrupleargument\commando = \commando[#1][#2][#3][#4] +%D \doquintupleargument\commando = \commando[#1][#2][#3][#4][#5] +%D \dosixtupleargument\commando = \commando[#1][#2][#3][#4][#5][#6] +%D \doseventupleargument\command = \commando[#1][#2][#3][#4][#5][#6][#7] +%D \stoptyping +%D +%D These macros are used in the following way: +%D +%D \starttyping +%D \def\dosetupsomething[#1][#2]% +%D {... #1 ... #2 ...} +%D +%D \def\setupsomething +%D {\dodoubleargument\dosetupsomething} +%D \stoptyping +%D +%D The implementation can be surprisingly simple and needs no +%D further explanation, like: +%D +%D \starttyping +%D \def\dosingleargument#1[#2]% +%D {#1[#2]} +%D \def\dotripleargument#1[#2]#3[#4]#5[#6]% +%D {#1[#2][#4][#6]} +%D \def\doquintupleargument#1% +%D {\def\dodoquintupleargument[##1]##2[##3]##4[##5]##6[##7]##8[##9]% +%D {#1[##1][##3][##5][##7][##9]}% +%D \dodoquintupleargument} +%D \stoptyping +%D +%D Because \TEX\ accepts 9~arguments at most, we have to use +%D two||step solution when getting five or more arguments. +%D +%D When developing more and more of the real \CONTEXT, we +%D started using some alternatives that provided empty +%D arguments (in fact optional ones) whenever the user failed +%D to supply them. Because this more complicated macros enable +%D us to do some checking, we reimplemented the non||empty +%D ones. + +\def\dosingleargument {\let\expectedarguments\plusone \dosingleempty } +\def\dodoubleargument {\let\expectedarguments\plustwo \dodoubleempty } +\def\dotripleargument {\let\expectedarguments\plusthree \dotripleempty } +\def\doquadrupleargument {\let\expectedarguments\plusfour \doquadrupleempty } +\def\doquintupleargument {\let\expectedarguments\plusfive \doquintupleempty } +\def\dosixtupleargument {\let\expectedarguments\plussix \dosixtupleempty } +\def\doseventupleargument{\let\expectedarguments\plusseven \doseventupleempty} + +%D \macros +%D {iffirstagument,ifsecondargument,ifthirdargument, +%D iffourthargument,iffifthargument,ifsixthargument, +%D ifseventhargument} +%D +%D We use some signals for telling the calling macros if all +%D wanted arguments are indeed supplied by the user. + +\newif\iffirstargument +\newif\ifsecondargument +\newif\ifthirdargument +\newif\iffourthargument +\newif\iffifthargument +\newif\ifsixthargument +\newif\ifseventhargument + +%D \macros +%D {dosingleempty,dodoubleempty,dotripleempty, +%D doquadrupleempty,doquintupleempty,dosixtupeempty, +%D doseventupleempty} +%D +%D The empty argument supplying macros mentioned before, look +%D like: +%D +%D \starttyping +%D \dosingleempty \command +%D \dodoubleempty \command +%D \dotripleempty \command +%D \doquadrupleempty \command +%D \doquintupleempty \command +%D \dosixtupleempty \command +%D \doseventupleempty\command +%D \stoptyping +%D +%D So \type{\dodoubleempty} leades to: +%D +%D \starttyping +%D \command[#1][#2] +%D \command[#1][] +%D \command[][] +%D \stoptyping +%D +%D Depending of the generousity of the user. Afterwards one can +%D use the \type{\if...argument} boolean. For novice: watch +%D the stepwise doubling of \type{#}'s + +% idea: \ignorespaces afterwards + +\chardef\noexpectedarguments=0 +\chardef\expectedarguments =0 + +\def\showargumenterror#1#2% + {\writestatus{systems}{\number#1 argument(s) expected in line #2}} + +\def\doshowargumenterror + {\ifnum\expectedarguments>\noexpectedarguments + \showargumenterror{\number\expectedarguments}{\number\inputlineno}% + \fi + \noshowargumenterror} + +\def\noshowargumenterror + {\let\expectedarguments\noexpectedarguments} + +\long\def\dogetargument#1#2#3#4% + {\let\charactertoken=#1% + \def\!!stringa{\noshowargumenterror#3\dodogetargument}% + \def\!!stringb{\doshowargumenterror#4\dodogetargument#1#2}% + \futurelet\nexttoken\inspectnextcharacter} + +\def\getsingleempty#1#2#3% + {\def\dodogetargument% + {#3}% + \dogetargument#1#2\firstargumenttrue\firstargumentfalse} + +\def\getdoubleempty#1#2#3% + {\def\dodogetargument#1##1#2% + {\def\dodogetargument% + {#3#1{##1}#2}% + \dogetargument#1#2\secondargumenttrue\secondargumentfalse}% + \dogetargument#1#2\firstargumenttrue\firstargumentfalse} + +\def\gettripleempty#1#2#3% + {\def\dodogetargument#1##1#2% + {\def\dodogetargument#1####1#2% + {\def\dodogetargument% + {#3#1{##1}#2% + #1{####1}#2}% + \dogetargument#1#2\thirdargumenttrue\thirdargumentfalse}% + \dogetargument#1#2\secondargumenttrue\secondargumentfalse}% + \dogetargument#1#2\firstargumenttrue\firstargumentfalse} + +\def\getquadrupleempty#1#2#3% + {\def\dodogetargument#1##1#2% + {\def\dodogetargument#1####1#2% + {\def\dodogetargument#1########1#2% + {\def\dodogetargument% + {#3#1{##1}#2% + #1{####1}#2% + #1{########1}#2}% + \dogetargument#1#2\fourthargumenttrue\fourthargumentfalse}% + \dogetargument#1#2\thirdargumenttrue\thirdargumentfalse}% + \dogetargument#1#2\secondargumenttrue\secondargumentfalse}% + \dogetargument#1#2\firstargumenttrue\firstargumentfalse} + +\def\getquintupleempty#1#2#3% + {\def\dodogetargument#1##1#2% + {\def\dodogetargument#1####1#2% + {\def\dodogetargument#1########1#2% + {\def\dodogetargument#1################1#2% + {\def\dodogetargument% + {#3#1{##1}#2% + #1{####1}#2% + #1{########1}#2% + #1{################1}#2}% + \dogetargument#1#2\fifthargumenttrue\fifthargumentfalse}% + \dogetargument#1#2\fourthargumenttrue\fourthargumentfalse}% + \dogetargument#1#2\thirdargumenttrue\thirdargumentfalse}% + \dogetargument#1#2\secondargumenttrue\secondargumentfalse}% + \dogetargument#1#2\firstargumenttrue\firstargumentfalse} + +\def\getsixtupleempty#1#2#3% + {\def\dodogetargument#1##1#2% + {\def\dodogetargument#1####1#2% + {\def\dodogetargument#1########1#2% + {\def\dodogetargument#1################1#2% + {\def\dodogetargument#1################################1#2% + {\def\dodogetargument% + {#3#1{##1}#2% + #1{####1}#2% + #1{########1}#2% + #1{################1}#2% + #1{################################1}#2}% + \dogetargument#1#2\sixthargumenttrue\sixthargumentfalse}% + \dogetargument#1#2\fifthargumenttrue\fifthargumentfalse}% + \dogetargument#1#2\fourthargumenttrue\fourthargumentfalse}% + \dogetargument#1#2\thirdargumenttrue\thirdargumentfalse}% + \dogetargument#1#2\secondargumenttrue\secondargumentfalse}% + \dogetargument#1#2\firstargumenttrue\firstargumentfalse} + +\def\getseventupleempty#1#2#3% + {\def\dodogetargument#1##1#2% + {\def\dodogetargument#1####1#2% + {\def\dodogetargument#1########1#2% + {\def\dodogetargument#1################1#2% + {\def\dodogetargument#1################################1#2% + {\def\dodogetargument#1################################% + ################################1#2% + {\def\dodogetargument% + {#3#1{##1}#2% + #1{####1}#2% + #1{########1}#2% + #1{################1}#2% + #1{################################1}#2% + #1{################################% + ################################1}#2}% + \dogetargument#1#2\seventhargumenttrue\seventhargumentfalse}% + \dogetargument#1#2\sixthargumenttrue\sixthargumentfalse}% + \dogetargument#1#2\fifthargumenttrue\fifthargumentfalse}% + \dogetargument#1#2\fourthargumenttrue\fourthargumentfalse}% + \dogetargument#1#2\thirdargumenttrue\thirdargumentfalse}% + \dogetargument#1#2\secondargumenttrue\secondargumentfalse}% + \dogetargument#1#2\firstargumenttrue\firstargumentfalse} + +\def\dosingleempty {\getsingleempty []} +\def\dodoubleempty {\getdoubleempty []} +\def\dotripleempty {\gettripleempty []} +\def\doquadrupleempty {\getquadrupleempty []} +\def\doquintupleempty {\getquintupleempty []} +\def\dosixtupleempty {\getsixtupleempty []} +\def\doseventupleempty{\getseventupleempty[]} + +%D Because some of these are called quite often, we will now +%D replace the more general version by alternatives tuned for +%D speed. + +\def\dosingleempty#1% we can make dedicated doifnextoptional's + {\noshowargumenterror % \relax % prevents lookahead, brr + \doifnextoptionalelse + {\firstargumenttrue#1}% + {\dosinglefakeempty#1}} + +\def\dodoubleempty#1% + {\noshowargumenterror % \relax % prevents lookahead, brr + \doifnextoptionalelse + {\dodoubletestempty#1}% + {\dodoublefakeempty#1}} + +\def\dotripleempty#1% + {\noshowargumenterror % \relax % prevents lookahead, brr + \doifnextoptionalelse + {\dotripletestempty#1}% + {\dotriplefakeempty#1}} + +\def\dosinglefakeempty#1% + {\firstargumentfalse#1[]} + +\def\dodoublefakeempty#1% + {\firstargumentfalse\secondargumentfalse#1[][]} + +\def\dotriplefakeempty#1% + {\firstargumentfalse\secondargumentfalse\thirdargumentfalse#1[][][]} + +\long\def\dodoubletestempty#1[#2]% + {\firstargumenttrue + \doifnextoptionalelse + {\secondargumenttrue #1[{#2}]}% + {\secondargumentfalse#1[{#2}][]}} + +\long\def\dotripletestempty#1[#2]% + {\firstargumenttrue + \doifnextoptionalelse + {\dotripletestemptyx #1[{#2}]}% + {\secondargumentfalse + \thirdargumentfalse #1[{#2}][][]}} + +\long\def\dotripletestemptyx#1[#2][#3]% + {\secondargumenttrue + \doifnextoptionalelse + {\thirdargumenttrue #1[{#2}][{#3}]}% + {\thirdargumentfalse#1[{#2}][{#3}][]}} + +%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}. + +\def\strippedcsname + {\expandafter\gobbleoneargument\string} + +%D \macros +%D {complexorsimple,complexorsimpleempty} +%D +%D Setups can be optional. A command expecting a setup is +%D prefixed by \type{\complex}, a command without one gets the +%D prefix \type{\simple}. Commands like this can be defined by: +%D +%D \starttyping +%D \complexorsimple\command +%D \stoptyping +%D +%D When \type{\command} is followed by a \type{[setup]}, then +%D +%D \starttyping +%D \complexcommand [setup] +%D \stoptyping +%D +%D executes, else we get +%D +%D \starttyping +%D \simplecommand +%D \stoptyping +%D +%D An alternative for \type{\complexorsimple} is: +%D +%D \starttyping +%D \complexorsimpleempty {command} +%D \stoptyping +%D +%D Depending on the presence of \type{[setup]}, this one +%D leads to one of: +%D +%D \starttyping +%D \complexcommando [setup] +%D \complexcommando [] +%D \stoptyping +%D +%D Many \CONTEXT\ commands started as complex or simple ones, +%D but changed into more versatile (more object oriented) ones +%D using the \type{\get..argument} commands. + +\def\complexorsimple#1% + {% \relax % prevents lookahead, brrr + \doifnextoptionalelse + {\firstargumenttrue \csname\s!complex\strippedcsname#1\endcsname} + {\firstargumentfalse\csname\s!simple \strippedcsname#1\endcsname}} + +\def\complexorsimpleempty#1% + {% \relax % prevents lookahead, brrr + \doifnextoptionalelse + {\firstargumenttrue \csname\s!complex\strippedcsname#1\endcsname} + {\firstargumentfalse\csname\s!complex\strippedcsname#1\endcsname[]}} + +%D \macros +%D {definecomplexorsimple,definecomplexorsimpleempty} +%D +%D The previous commands are used that often that we found it +%D worthwile to offer two more alternatives. Watch the build +%D in protection. + +\def\docomplexorsimple#1#2% + {\doifnextoptionalelse{\firstargumenttrue#1}{\firstargumentfalse#2}} + +\def\docomplexorsimpleempty#1% + {\doifnextoptionalelse{\firstargumenttrue#1}{\firstargumentfalse#1[]}} + +\def\definecomplexorsimple#1% + {\unexpanded\edef#1% + {\noexpand\docomplexorsimple + \@EA\noexpand\csname\s!complex\strippedcsname#1\endcsname + \@EA\noexpand\csname\s!simple \strippedcsname#1\endcsname}} + +\def\definecomplexorsimpleempty#1% + {\unexpanded\edef#1% + {\noexpand\docomplexorsimpleempty + \@EA\noexpand\csname\s!complex\strippedcsname#1\endcsname}} + +%D These commands are called as: +%D +%D \starttyping +%D \definecomplexorsimple\command +%D \stoptyping +%D +%D Of course, we must have available +%D +%D \starttyping +%D \def\complexcommand[#1]{...} +%D \def\simplecommand {...} +%D \stoptyping +%D +%D Using this construction saves a few string now and then. + +%D \macros +%D {dosinglegroupempty,dodoublegroupempty,dotriplegroupempty, +%D doquadruplegroupempty, doquintuplegroupempty} +%D +%D We've already seen some commands that take care of +%D optional arguments between \type{[]}. The next two commands +%D handle the ones with \type{{}}. They are called as: +%D +%D \starttyping +%D \dosinglegroupempty \ineedONEargument +%D \dodoublegroupempty \ineedTWOarguments +%D \dotriplegroupempty \ineedTHREEarguments +%D \doquadruplegroupempty \ineedFOURarguments +%D \doquintuplegroupempty \ineedFIVEarguments +%D \stoptyping + +%D We can add additional definitions later when we have defined +%D \type {\appendtoks}. + +\def \permitspacesbetweengroups{\let\@@permitspacesbetweengroups\zerocount} +\def\dontpermitspacesbetweengroups{\let\@@permitspacesbetweengroups\plusone} + +\dontpermitspacesbetweengroups + +%D We can avoid the nasty if handling in \type {syst-gen} by splitting +%D the lot in pieces so that we have no nested \type {\nextarguments} +%D potentially being an \type {conditional} token. Okay, these macros +%D are not called that often but it saves crap when tracing. + +% \def\dogetgroupargument#1#2% +% {\let\dogroupargumentyes#1% +% \let\dogroupargumentnop#2% +% \futurelet\nextargument\dodogetgroupargument} + +\def\dodogetgroupargument + {\ifx\nextargument\bgroup + \expandafter\dodogetgroupargumentA + \else + \expandafter\dodogetgroupargumentB + \fi} + +\def\dodogetgroupargumentA + {\noshowargumenterror + \dogroupargumentyes\dodogetargument} + +% \def\dodogetgroupargumentB +% {\ifcase\@@permitspacesbetweengroups +% \expandafter\dodogetgroupargumentC +% \else +% \expandafter\dodogetgroupargumentD +% \fi} + +% \def\dodogetgroupargumentC +% {\ifx\nextargument\lineending +% \expandafter\dodogetgroupargumentE +% \else +% \expandafter\dodogetgroupargumentF +% \fi} + +\def\dodogetgroupargumentB + {\ifcase\@@permitspacesbetweengroups + \expandafter\dodogetgroupargumentF + \else + \expandafter\dodogetgroupargumentD + \fi} + +\def\dodogetgroupargumentD + {\doshowargumenterror + \dogroupargumentnop\dodogetargument{}} + +% \def\dodogetgroupargumentE +% {\begingroup\def\\ {\endgroup\dogetgroupargument\dogroupargumentyes\dogroupargumentnop}\\} + +\begingroup + \def\\ {\dogetgroupargument\dogroupargumentyes\dogroupargumentnop} + \global\let\dodogetgroupargumentE\\ +\endgroup + +\def\dodogetgroupargumentF + {\ifx\nextargument\blankspace + \expandafter\dodogetgroupargumentE % G + \else + \expandafter\dodogetgroupargumentD % H + \fi} + +\def\dogetgroupargument#1#2% + {\let\dogroupargumentyes#1% + \let\dogroupargumentnop#2% + \futurelet\nextargument\dodogetgroupargument} + +\def\dosinglegroupempty#1% + {\def\dodogetargument% + {\dontpermitspacesbetweengroups + #1}% + \dogetgroupargument\firstargumenttrue\firstargumentfalse} + +\def\dodoublegroupempty#1% + {\def\dodogetargument##1% + {\def\dodogetargument% + {\dontpermitspacesbetweengroups + #1{##1}}% + \dogetgroupargument\secondargumenttrue\secondargumentfalse}% + \dogetgroupargument\firstargumenttrue\firstargumentfalse} + +\def\dotriplegroupempty#1% + {\def\dodogetargument##1% + {\def\dodogetargument####1% + {\def\dodogetargument% + {\dontpermitspacesbetweengroups + #1{##1}{####1}}% + \dogetgroupargument\thirdargumenttrue\thirdargumentfalse}% + \dogetgroupargument\secondargumenttrue\secondargumentfalse}% + \dogetgroupargument\firstargumenttrue\firstargumentfalse} + +\def\doquadruplegroupempty#1% + {\def\dodogetargument##1% + {\def\dodogetargument####1% + {\def\dodogetargument########1% + {\def\dodogetargument% + {\dontpermitspacesbetweengroups + #1{##1}{####1}{########1}}% + \dogetgroupargument\fourthargumenttrue\fourthargumentfalse}% + \dogetgroupargument\thirdargumenttrue\thirdargumentfalse}% + \dogetgroupargument\secondargumenttrue\secondargumentfalse}% + \dogetgroupargument\firstargumenttrue\firstargumentfalse} + +\def\doquintuplegroupempty#1% + {\def\dodogetargument##1% + {\def\dodogetargument####1% + {\def\dodogetargument########1% + {\def\dodogetargument################1% + {\def\dodogetargument% + {\dontpermitspacesbetweengroups + #1{##1}{####1}{########1}{################1}}% + \dogetgroupargument\fifthargumenttrue\fifthargumentfalse}% + \dogetgroupargument\fourthargumenttrue\fourthargumentfalse}% + \dogetgroupargument\thirdargumenttrue\thirdargumentfalse}% + \dogetgroupargument\secondargumenttrue\secondargumentfalse}% + \dogetgroupargument\firstargumenttrue\firstargumentfalse} + +%D These macros can explictly take care of spaces, which means +%D that the next definition and calls are valid: +%D +%D \starttyping +%D \def\test#1#2#3{[#1#2#3]} +%D +%D \dotriplegroupempty\test {a}{b}{c} +%D \dotriplegroupempty\test {a}{b} +%D \dotriplegroupempty\test {a} +%D \dotriplegroupempty\test +%D \dotriplegroupempty\test {a} {b} {c} +%D \dotriplegroupempty\test {a} {b} +%D \dotriplegroupempty\test +%D {a} +%D {b} +%D \stoptyping +%D +%D And alike. + +%D \macros +%D {firstofoneargument, firstoftwoarguments, firstofthreearguments +%D secondoftwoarguments, secondofthreearguments, +%D thirdofthreearguments} +%D +%D The next six macros (dedicated to Taco) can conveniently +%D used to select arguments. Their names explain their +%D functionality. + +\long\def\firstofoneargument #1{#1} + +\long\def\firstoftwoarguments #1#2{#1} +\long\def\secondoftwoarguments #1#2{#2} + +\long\def\firstofthreearguments #1#2#3{#1} +\long\def\secondofthreearguments #1#2#3{#2} +\long\def\thirdofthreearguments #1#2#3{#3} + +\long\def\firstoffourarguments #1#2#3#4{#1} +\long\def\secondoffourarguments #1#2#3#4{#2} +\long\def\thirdoffourarguments #1#2#3#4{#3} +\long\def\fourthoffourarguments #1#2#3#4{#4} + +\long\def\firstoffivearguments #1#2#3#4#5{#1} +\long\def\secondoffivearguments #1#2#3#4#5{#2} +\long\def\thirdoffivearguments #1#2#3#4#5{#3} +\long\def\fourthoffivearguments #1#2#3#4#5{#4} +\long\def\fifthoffivearguments #1#2#3#4#5{#5} + +\long\def\firstofsixarguments #1#2#3#4#5#6{#1} +\long\def\secondofsixarguments#1#2#3#4#5#6{#2} +\long\def\thirdofsixarguments #1#2#3#4#5#6{#3} +\long\def\fourthofsixarguments#1#2#3#4#5#6{#4} +\long\def\fifthofsixarguments #1#2#3#4#5#6{#5} +\long\def\sixthofsixarguments #1#2#3#4#5#6{#6} + +%D \macros +%D {globalletempty,letempty,letvalueempty,letgvalueempty} +%D +%D Trivial: + +\def\letempty #1{\let#1\empty} +\def\globalletempty#1{\global\let#1\empty} + +\def\letvalueempty #1{\expandafter\let\csname#1\endcsname\empty} +\def\letgvalueempty#1{\global\expandafter\let\csname#1\endcsname\empty} + +%D \macros +%D {wait} +%D +%D The next macro hardly needs explanation. Because no +%D nesting is to be expected, we can reuse \type{\wait} within +%D \type{\wait} itself. + +\def\wait + {\begingroup + \read16 to \wait + \endgroup} + +%D \macros +%D {writestring,writeline,writebanner, +%D writestatus,statuswidth,normalwritestatus} +%D +%D Maybe one didn't notice, but we've already introduced a +%D macro for showing messages. In the multi||lingual modules, +%D we will also introduce a mechanism for message passing. For +%D the moment we stick to the core macros: +%D +%D \starttyping +%D \writestring {string} +%D \writeline +%D \writestatus {category} {message} +%D \stoptyping +%D +%D Messages are formatted. One can provide the maximum with +%D of the identification string with the macro \type +%D {\statuswidth}. + +\chardef\statuswidth=15 +\chardef\statuswrite=16 + +\ifdefined\writestring \else + + \newtoks\everywritestring + + \def\writedirect {\immediate\write\statuswrite} + \def\writeline {\writedirect{}} + \def\writestring#1{\begingroup\the\everywritestring\writedirect{#1}\endgroup} + +\fi + +\def\normalwritestatus#1#2% + {\writestring{\expandafter\dosplitstatus\expandafter\statuswidth#1% + \space\space\space\space\space\space\space + \space\space\space\space\space\space\space + \space\space\space\space\space\space\end + \space:\space#2}} + +\def\dosplitstatus#1#2% + {\ifcase#1 \expandafter\nosplitstatus\fi#2% + \expandafter\dosplitstatus\expandafter{\the\numexpr#1+\minusone\relax}} + +\def\nosplitstatus#1\end + {} + +%D \macros +%D {debuggerinfo} +%D +%D For debugging purposes we can enhance macros with the +%D next alternative. Here \type{debuggerinfo} stands for both +%D a macro accepting two arguments and a boolean (in fact a +%D few macro's too). + +\newif\ifdebuggerinfo + +\def\debuggerinfo#1#2% + {\ifdebuggerinfo + \writestatus{debugger}{#1:: #2}% + \fi} + +\ifdefined\writestatus \else \let\writestatus\normalwritestatus \fi +\ifdefined\writebanner \else \def\writebanner{\writestring} \fi + +% % % % % % % % % % % % % % % % % % % % % % % % + +%D \macros +%D {rawgetparameters} +%D +%D A raw and dirty alternative for \type {\getparameters}; no +%D checking is done! + +\def\rawsetparameter#1=#2,% + {\if]#1\else + \expandafter\def\csname\rawparameterprefix#1\endcsname{#2}% + \expandafter\rawsetparameter + \fi} + +\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\resetglobal + {\let\redoglobal\relax + \let\dodoglobal\relax} + +\resetglobal + +\def\doglobal + {\ifx\redoglobal\relax + \let\redoglobal\global + \let\dodoglobal\@@dodoglobal + \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% + {\ifdefined#1% + \message{[\noexpand#1is already defined]}% + \expandafter\def\expandafter\gobbleddefinition + \else + \expandafter\def + \fi#1} + +\def\redefine#1% + {\ifdefined#1% + \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 + +% \starttexdefinition test +% oeps +% \stoptexdefinition +% +% [\test] + +\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} + +%D Nowadays we don't mind a few more tokens if we can gain a +%D bit of speed. + +\def\doincrement#1% + {\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi+\plusone \relax}} +\def\dodecrement#1% + {\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi+\minusone\relax}} + +\def\dododoincrement#1,#2)% + {\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi+#2\relax}} +\def\dodododecrement#1,#2)% + {\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi-#2\relax}} + +\def\dodoincrement(#1% + {\doifnextcharelse,{\dododoincrement#1}{\dododoincrement#1,\plusone}} +\def\dododecrement(#1% + {\doifnextcharelse,{\dodododecrement#1}{\dodododecrement#1,\plusone}} + +\def\fastincrement#1{\dodoglobal\edef#1{\the\numexpr#1+\plusone \relax}} +\def\fastdecrement#1{\dodoglobal\edef#1{\the\numexpr#1+\minusone\relax}} + +\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% + {\ifdefined#1\else + \advance\maximumsignal 2sp % to be save in rounding + \edef#1{\the\maximumsignal}% + \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 + +\ifdefined\letterbackslash \else + {\catcode`.=0 .catcode`.\ 12 .xdef.letterbackslash{.string\}} % hack +\fi + +\def\checkedstrippedcsname#1% this permits \strippedcsname{\xxx} and \strippedcsname{xxx} + {\expandafter\docheckedstrippedcsname\string#1} + +\def\docheckedstrippedcsname#1% + {\if\noexpand#1\letterbackslash\else#1\fi} + +%D \macros +%D {savenormalmeaning} +%D +%D We will use this one in: + +\def\savenormalmeaning#1% + {\ifcsname normal\strippedcsname#1\endcsname \else + \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} + +\def\settrue #1{\let#1\zerocount} +\def\setfalse#1{\let#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 + +\def\@@irecurse{@@ir@@} % ecurse} % stepper +\def\@@arecurse{@@ar@@} % ecurse} % action + +\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\normalexpanded{\noexpand\nextrecurse{\number#1}{\number#2}{\number#3}}} + +\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}} + +\def\expandrecursecontent + {\csname\@@arecurse\recursedepth\endcsname} + +\def\redostepwiserecurse + {\expandrecursecontent\dodostepwiserecurse} + +\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}} + +\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}} + +\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} + +\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}} + +\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}} + +\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}. + +\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 + +\long\def\dodoloop#1% + {\def\recurselevel{#1}% + \@EA\redoloop\@EA{\the\numexpr\recurselevel+\plusone\relax}} + +\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\normalexpanded{\noexpand\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 + +% EXPERIMENT + +% faster + +\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>\zerocount + \ifnum#2<#1\relax + \let\nextrecurse\exitstepwiserecurse + \else + \let\nextrecurse\dodostepwiserecurse + \fi + \else + \ifnum#3<\zerocount + \ifnum#1<#2\relax + \let\nextrecurse\exitstepwiserecurse + \else + \let\nextrecurse\dodostepwisereverse + \fi + \else + \let\nextrecurse\exitstepwiserecurse + \fi + \fi + \expandafter\nextrecurse\normalexpanded{{\number#1}{\number#2}{\number#3}}} + +% slightly faster + +\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 + \csname @swr% + \ifnum#3>\zerocount + \ifnum#2<#1\else d\fi + \else\ifnum#3<\zerocount + \ifnum#1<#2\else r\fi + \fi\fi + \expandafter\endcsname\normalexpanded{{\number#1}{\number#2}{\number#3}}} + +\let\@swr \exitstepwiserecurse +\let\@swrd\dodostepwiserecurse +\let\@swrr\dodostepwisereverse + +%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. + +% \dorecurse{2}{ +% \expanded{\everypar{before \recurselevel\space}} +% \EveryPar{x } [before \recurselevel\space x] \par +% \EveryPar{y } [before \recurselevel\space y] \par +% \EveryPar{} [before \recurselevel] \par +% \EveryPar{x } \EveryPar{y } \EveryPar{} [before \recurselevel] \par +% \EveryPar{y } \everypar{before } [before] \par +% } + +% retrofit this into mkii + +\def\dowithevery#1% + {\expandafter\removetoks\expandafter\the\csname t\strippedcsname#1\endcsname\from#1% + \expandafter\appendtoks\expandafter\the\csname t\strippedcsname#1\endcsname\to #1% + \csname t\strippedcsname#1\endcsname} + +\def\newevery#1#2% + {\newtoks#1% we test for redefinition elsewhere + \ifx#2\relax\else\ifdefined#2\else + \expandafter\newtoks\csname t\strippedcsname#1\endcsname + \def#2{\dowithevery#1}% + \fi\fi} + +%D This one permits definitions like: + +\newevery \everypar \EveryPar % we get a warning which is ok +\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. + +\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 + +\long\def\docheckifassignmentelse#1=#2#3\@end@{\if#2@}% + +\long\def\doifassignmentelse#1% + {\expandafter\docheckifassignmentelse\detokenize{#1}=@@\@end@ + \expandafter\secondoftwoarguments + \else + \expandafter\firstoftwoarguments + \fi} + +% 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% +% {\@EA#1\@EA{\detokenize{#2}}} + +%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). + +\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}}} + +%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} + +\def\showvalue#1% + {\ifcsname#1\endcsname + \expandafter\show\csname#1\endcsname + \else + \show\undefined + \fi} + +%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. +%D +%D \starttyping +%D \doifelse {\jobname}{oeps}{YES}{NO} +%D \doifsamestringelse{\jobname}{oeps}{YES}{NO} +%D \stoptyping + +% \def\@@doifsamestringelse#1#2#3#4% +% {\edef\!!stringa{#3}\convertcommand\!!stringa\to\!!stringa +% \edef\!!stringb{#4}\convertcommand\!!stringb\to\!!stringb +% \ifx\!!stringa\!!stringb\expandafter#1\else\expandafter#2\fi} + +\def\@@doifsamestringelse#1#2#3#4% + {\edef\!!stringa{\detokenize\expandafter{\normalexpanded{#3}}}% + \edef\!!stringb{\detokenize\expandafter{\normalexpanded{#4}}}% + \ifx\!!stringa\!!stringb\expandafter#1\else\expandafter#2\fi} + +\def\doifsamestringelse{\@@doifsamestringelse\firstoftwoarguments\secondoftwoarguments} +\def\doifsamestring {\@@doifsamestringelse\firstofoneargument\gobbleoneargument} +\def\doifnotsamestring {\@@doifsamestringelse\gobbleoneargument\firstofoneargument} + +%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. + +\long\def\ConvertToConstant#1#2#3% + {\edef\!!stringa{\expandafter\detokenize\expandafter{#2}}% + \edef\!!stringb{\expandafter\detokenize\expandafter{#3}}% + #1{\!!stringa}{\!!stringb}} + +%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 \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. We also handle +%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 I considered it a 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% + {\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 + +\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} + +\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 + +\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 + +\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\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% + \normalexpanded{\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. + +\newtoks\@@scratchtoks + +\def\appendtoks {\doappendtoks \relax} +\def\prependtoks {\doprependtoks \relax} +\def\appendtoksonce {\doappendtoksonce \relax} +\def\prependtoksonce{\doprependtoksonce\relax} + +\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: + +\def\doifintokselse#1#2% #1 en #2 zijn toks + {\edef\asciia{\detokenize\expandafter{\the#1}}% + \edef\asciib{\detokenize\expandafter{\the#2}}% + \doifstringinstringelse\asciia\asciib} + +%D A nice one too: + +% {\scratchtoks{abc} \removetoks b\from\scratchtoks [\the\scratchtoks]} +% {\scratchtoks{abc} \removetoks x\from\scratchtoks [\the\scratchtoks]} +% {\scratchtoks{} \removetoks x\from\scratchtoks [\the\scratchtoks]} +% {\scratchtoks{xaa} \removetoks x\from\scratchtoks [\the\scratchtoks]} +% {\scratchtoks{a\relax b} \removetoks \relax\from\scratchtoks [\showthe\scratchtoks]} + +\def\removetoks#1\from#2% + {\def\doremovetoks##1#1##2\empty\empty\empty##3\\% + {\def\!!stringa{##3}% + \ifx\!!stringa\empty#2{##1}\else#2{##1##2}\fi}% + \expandafter\doremovetoks\the#2\empty\empty\empty#1\empty\empty\empty\\} + +%D Also: + +\def\appendetoks #1\to{\normalexpanded{\noexpand\appendtoks #1}\to} +\def\prependetoks#1\to{\normalexpanded{\noexpand\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} + +% better: \def\flushtoks#1{\normalexpanded{\noexpand\dodoglobal#1\emptytoks\the#\relax}} + +\let\dotoks\the + +%D \macros +%D {makecounter,pluscounter,minuscounter, +%D resetcounter,setcounter,countervalue} +%D +%D Declaring, setting and resetting \COUNTERS\ can be done +%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 + +\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}} + +%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\\} + +\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} + +\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% + {\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}} + +\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% + {\edef\!!stringa{\detokenize\expandafter{\normalexpanded{#1}}}% + \edef\!!stringb{\detokenize\expandafter{\normalexpanded{#2}}}% + \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% to be sped up + {\rawdoifinsetelse{#1}#2% + {\normalexpanded{\noexpand\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 {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% + {\withoutpt\the\dimexpr0.0351459804\dimexpr#1\relax\relax cm} + +%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\@sl@{@sl@} +\def\@sg@{@sg@} + +\let\@@pushedmacro\empty + +\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 } + +\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% 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]% + {\doifsomething{#1} + {\def\@@dodowithsetcommand##1{\@@dowithsetcommand[##1][#2]}% + \processcommalist[#1]\@@dodowithsetcommand}} + +\def\dotriplewithset[#1][#2][#3]% + {\doifsomething{#1} + {\def\@@dodowithsetcommand##1{\@@dowithsetcommand[##1][#2][#3]}% + \processcommalist[#1]\@@dodowithsetcommand}} + +\def\dodoubleemptywithset #1{\let\@@dowithsetcommand#1\dodoubleempty \dodoublewithset} % \command +\def\dodoubleargumentwithset#1{\let\@@dowithsetcommand#1\dodoubleargument\dodoublewithset} % \command + +\def\dotripleemptywithset #1{\let\@@dowithsetcommand#1\dotripleempty \dotriplewithset} % \command +\def\dotripleargumentwithset#1{\let\@@dowithsetcommand#1\dotripleargument\dotriplewithset} % \command + +%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{{}}. + +\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 / never change this one again + {\ifcsname#1\endcsname + \csname#1\expandafter\expandafter\expandafter\endcsname\expandafter\gobbleoneargument + \else + \expandafter\firstofoneargument + \fi} + +%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 {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? + +\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 + \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 +%D 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% + {\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 {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} + +%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. + +% \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 + +\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}} + +\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} + +%D But, if you run a recent version of \TEX, we can use the new +%D primitive: + +\ifdefined\normalquitvmode \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 + {\uppercase\@EA{\@EA\dodoglobal\@EA\edef\@EA#2\@EA{\normalexpanded{#1}}}} + +\def\lowercasestring#1\to#2% first @EA redundant + {\lowercase\@EA{\@EA\dodoglobal\@EA\edef\@EA#2\@EA{\normalexpanded{#1}}}} + +%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 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 \somecount +%D \counttokens string\to \somecount +%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% + {\scratchcounter\zerocount + \def\!!stringa{#1}% + \def\!!stringb{\end}% + \def\docounttoken##1% obeys {} + {\def\!!stringc{##1}% + \ifx\!!stringb\!!stringc \else + \ifx\!!stringa\!!stringc + \advance\scratchcounter\plusone + \fi + \expandafter\docounttoken + \fi}% + \docounttoken#2\end + \dodoglobal#3\scratchcounter} + +\def\counttokens#1\to#2% + {\scratchcounter\zerocount + \def\docounttoken##1{\advance\scratchcounter\plusone}% + \handletokens#1\with\docounttoken + \dodoglobal#2\scratchcounter} + +%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% slow but hardly used + {\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} + +\def\dodohandletokens + {\ifx\nexthandledtoken\blankspace + \expandafter\dodohandletokensone + \else\ifx\nexthandledtoken\end + \expandafter\expandafter\expandafter\gobbletwoarguments % also gobble the \end + \else + \expandafter\expandafter\expandafter\dodohandletokenstwo + \fi\fi *} + +\def\dodohandletokensone * % + {\dododohandletokens{ }\dohandletokens} + +\long\def\dodohandletokenstwo *#1% + {\dododohandletokens{#1}\dohandletokens} + +%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% + {\scratchcounter\globaldefs + \ifnum\globaldefs#1\zerocount + \globaldefs-\globaldefs + \fi + \advance\globaldefs#2\plusone + \expandafter\chardef\csname@gd@\the\globaldefs\endcsname\scratchcounter} + +\def\dostopglobaldefs + {\globaldefs\ifcsname @gd@\the\globaldefs\endcsname\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. + +\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} + +%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} + +\def\doprocesscontent#1#2#3% + {\long\def\doprocesscontent##1#1% + {\endgroup\long\def#2{##1}#3}% + \doprocesscontent} + +%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. + +% 0: both are equal, 1: #1 is larger, 2: #2 is larger + +\def\thenumericcompare#1#2% no \relax es inside hee + {\doifnumberelse{#1} + {\doifnumberelse{#2}{\ifnum#1>#2 \plusone\else\ifnum#1<#2 \plustwo\else\zerocount\fi\fi}\plustwo} + \plusone} + +\def\donumericcompare + {\comparedresult\thenumericcompare} + +%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. + +\let\verbatimstring\detokenize + +%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 % no need to be fast + {\expandafter \expandafter \expandafter \strippedcsname + \ifcase#1\or \ifcase#2\or + \firstofoneargument \else + \gobbleoneargument \fi + \or \ifcase#2\or + \firstoftwoarguments \or + \secondoftwoarguments \else + \gobbletwoarguments \fi + \or \ifcase#2\or + \firstofthreearguments \or + \secondofthreearguments \or + \thirdofthreearguments \else + \gobblethreearguments \fi + \or \ifcase#2\or + \firstoffourarguments \or + \secondoffourarguments \or + \thirdoffourarguments \or + \fourthoffourarguments \else + \gobblefourarguments \fi + \or \ifcase#2\or + \firstoffivearguments \or + \secondoffivearguments \or + \thirdoffivearguments \or + \fourthoffivearguments \or + \fifthoffivearguments \else + \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]% + {\expandafter\def\csname\??dm#1\endcsname{#2}} + +% #2 could be omitted, but we want to support spaces +% +% \setmeasure {x} {1cm} +% \setmeasure {xx} {1cm} +% \setmeasure {xxx}{1cm} + +\def\setmeasure #1#2{\expandafter\def \csname\??dm#1\endcsname{#2}} % quick way +\def\setemeasure#1#2{\expandafter\edef\csname\??dm#1\endcsname{#2}} % quick way +\def\setgmeasure#1#2{\expandafter\gdef\csname\??dm#1\endcsname{#2}} % quick way +\def\setxmeasure#1#2{\expandafter\xdef\csname\??dm#1\endcsname{#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} + +\def\comparedimensioneps#1#2% todo: use eps feature + {\chardef\compresult + \ifdim\dimexpr#1-#2\relax<\roudingeps + \zerocount + \else\ifdim\dimexpr#2-#1\relax<\roudingeps + \zerocount + \else\ifdim#1<#2% + \plusone + \else + \plustwo + \fi\fi\fi} + +% % % % % % % % % % % % % % % % % % % % % % + +% pretty ugly but fast + +% \copycsname xxx\endcsname\csname ..\endcsname + +\def\copycsname{\@EA\@EA\@EA\let\@EA\@EA\csname} + +% \letcscsname \crap \csname ..\endcsname +% \letcsnamecs \csname ..\endcsname\crap +% \letcsnamecsname\csname ..\endcsname\csname ..\endcsname + +\def\letcscsname {\@EA\let\@EA} +\def\letcsnamecs {\@EA\let} +\def\letcsnamecsname{\@EA\@EA\@EA\let\@EA\@EA} + +% another one, add an item to a commalist + +\def\addvalue#1#2% cs item + {\ifcsname#1\endcsname\else\expandafter\let\csname#1\endcsname\empty\fi + \normalexpanded{\noexpand\addtocommalist{#2}\@EA\noexpand\csname#1\endcsname}} + +\def\unspaced#1% + {\dounspaced#1\end} + +\def\dounspaced#1% + {\ifx#1\end + \@EA\gobbleoneargument + \else + \ifx#1\blankspace\else#1\fi + \fi + \dounspaced} + +\def\unspaceargument#1\to#2% + {\scratchcounter\catcode32\relax + \catcode32\@@ignore\scantextokens{\edef#2{#1}}% + \catcode32\scratchcounter} + +\def\unspaceafter#1#2% + {\unspaceargument#2\to\ascii + \expandafter#1\expandafter{\ascii}} + +% sometimes handy: + +\def\doifhasspaceelse#1% + {\edef\!!stringa{#1}% + \normalexpanded{\noexpand\dodoifhasspaceelse#1\space}\empty\relax} + +\def\dodoifhasspaceelse#1 #2#3\relax % \space\empty\relax + {\ifx\!!stringa\space + \@EA\firstoftwoarguments + \else\ifx#2\empty + \@EAEAEA\secondoftwoarguments + \else + \@EAEAEA\firstoftwoarguments + \fi\fi} + +% this will replace loadfile once and alike !!! todo + +\def\@flg@{@flg@} + +\def\setflag #1{\@EA\dodoglobal\@EA\let\csname\@flg@#1\endcsname\zerocount} +\def\resetflag#1{\@EA\dodoglobal\@EA\let\csname\@flg@#1\endcsname\plusone} + +\let\ifflagged\ifcase + +\def\flag#1{\csname\@flg@#1\endcsname} + +\def\doifelseflagged#1% + {\@EA\ifx\csname\@flg@#1\endcsname\relax + \@EA\secondoftwoarguments + \else\ifcase\csname\@flg@#1\endcsname + \@EAEAEA\firstoftwoarguments + \else + \@EAEAEA\secondoftwoarguments + \fi\fi} + +\def\doifnotflagged#1% + {\@EA\ifx\csname\@flg@#1\endcsname\relax + \@EA\firstofoneargument + \else\ifcase\csname\@flg@#1\endcsname + \@EAEAEA\gobbleoneargument + \else + \@EAEAEA\firstofoneargument + \fi\fi} + +\def\inheritparameter[#1]#2[#3]#4[#5]% tag tokey fromkey % [bypasses k!prefix] + {\@EA\def\csname#1#3\@EA\endcsname\@EA{\csname#1#5\endcsname}} + +% \buildarray[test][aa,bb,cc,dd,ee,ff] +% \setarrayelement{test}{1}{qq} +% \arrayelement{test}{1} +% \arraylength{test} +% +% \def\buildarray[#1][#2]% +% {\scratchcounter=0 +% \def\docommand##1% +% {\advance\scratchcounter by 1 +% \setvalue{@@aa#1\the\scratchcounter}{##1}}% +% \processcommalist[#2]\docommand +% \setevalue{@@aa#1}{\the\scratchcounter}}% +% +% \def\setarrayelement#1#2{\setvalue{@@aa#1#2}} +% \def\arrayelement #1#2{\getvalue{@@aa#1#2}} +% \def\arraylength #1{\getvalue{@@aa#1}} + +% \newsignal\junksignal +% +% \def\setjunksignal% +% {\ifhmode +% \hskip\junksignal +% \let\removejunkspaces\doremovejunkspaces +% \else +% \let\removejunkspaces\relax +% \fi} +% +% \def\doremovejunkspaces% +% {\doloop{\ifdim\lastskip=\junksignal\unskip\else\exitloop\fi}} + +\def\dodoifnonzeropositiveelse#1#2\end % #3#4% + {\ifx#1\relax + \ifcase\scratchcounter + \endgroup + \@EAEAEA\secondoftwoarguments + \else + \endgroup + \@EAEAEA\firstoftwoarguments + \fi + \else + \endgroup + \@EA\secondoftwoarguments + \fi} + +\def\doifnonzeropositiveelse#1% + {\begingroup\afterassignment\dodoifnonzeropositiveelse\scratchcounter=0#1\relax\empty\end} + +% here ? + +\def\dosetrawvalue #1#2#3{\@EA \def\csname#1#2\endcsname{#3}} +\def\dosetrawevalue#1#2#3{\@EA\edef\csname#1#2\endcsname{#3}} +\def\dosetrawgvalue#1#2#3{\@EA\gdef\csname#1#2\endcsname{#3}} +\def\dosetrawxvalue#1#2#3{\@EA\xdef\csname#1#2\endcsname{#3}} + +\def\getrawparameters {\dogetparameters\dosetrawvalue } +\def\getraweparameters {\dogetparameters\dosetrawevalue} +\def\getrawgparameters {\dogetparameters\dosetrawgvalue} +\def\getrawxparameters {\dogetparameters\dosetrawxvalue} + +\def\globalgetrawparameters{\dogetparameters\dosetrawgvalue} % obsolete + +\def\splitskip#1% + {\scratchskip#1\relax + \dimen0\scratchskip + \dimen2\gluestretch\scratchskip + \dimen4\glueshrink\scratchskip} + +\newcount\modcounter + +\def\dosetmodulo#1#2#3% + {\modcounter#1\divide\modcounter#2\multiply\modcounter#2% + #3#1\advance#3-\modcounter} + +\def\dosetdivision#1#2#3% + {#3#1\divide#3 #2\relax} + +\def\DoMod#1by#2to#3{\dosetmodulo {#1}{#2}{#3}} +\def\DoDiv#1by#2to#3{\dosetdivision{#1}{#2}{#3}} + +\def\dounprotected#1\par + {#1\protect} + +\def\unprotected + {\unprotect\dounprotected} + +% awaiting the definitive implementation + +\ifdefined\resettimer \else + \let\resettimer \relax + \newcount\elapsedtime +\fi + +\newcount\featuretest + +\def\testfeature#1#2% + {\def\dotestfeature + {\advance\featuretest \plusone + \ifnum\featuretest>#1\else#2\expandafter\dotestfeature\fi}% + \retestfeature} + +\def\retestfeature % timer support is new per 10/5/2005 + {\bgroup + \ifcase\interactionmode\let\wait\relax\fi + \writestatus\m!systems{starting feature test}\wait + \resettimer + \featuretest\zerocount \dotestfeature + \writestatus\m!systems{feature test done (\elapsedseconds s)}% + \wait + \egroup} + +\def\elapsedseconds{\expandafter\withoutpt\the\dimexpr\elapsedtime sp\relax} + +\def\showtimer#1% + {\writestatus{runtime}{\elapsedseconds\space s / #1}} + +\def\testfeatureonce#1#2% + {\let\wait\relax\testfeature{#1}{#2}\end} + +%D \macros +%D {freezedimenmacro} +%D +%D This macro is use as: +%D +%D \starttyping +%D \freezedimenmacro\leftmargindistance +%D \stoptyping + +\def\freezedimenmacro#1% + {\edef#1{\the\dimexpr#1}} + +%D The next macro negates a macro (dimension or number, or actually, whatever. +%D It's a typical example of \type {\if} usage: +%D +%D \starttyping +%D \if-\whatever \else-\whatever\fi => else => -whatever +%D \if--\whatever\else-\whatever\fi => then => whatever +%D \stoptyping + +\def\negated#1{\if-#1\else-#1\fi} % does only work in macros or text + +% This permits things like ^\index{hans}^, where hans is +% duplicated in the text. + +\newif\ifduplicate + +\bgroup + +\gdef\checkduplication % in line with Knuth + {\ifmmode\expandafter^\else\expandafter\startduplication\fi} + +\gdef\insideduplication + {\ifmmode\expandafter^\else\expandafter\egroup\fi} + +\catcode`\^=\@@active + +\gdef\enableduplication + {\catcode`\^=\@@active \let^\checkduplication} + +\gdef\disableduplication + {\catcode`\^=\@@superscript} + +\gdef\startduplication + {\bgroup \duplicatetrue \let^\insideduplication} + +\egroup + +\def\gobbleassigndimen#1\\{} + +\def\assigndimen#1#2% + {\afterassignment\gobbleassigndimen#1=#2\!!zeropoint\\} + +\def\setusage#1% + {\@EA\let\csname#1\endcsname\iftrue} + +\def\resetusage#1% + {\@EA\let\csname#1\endcsname\iffalse} + +\def\ifusage#1% + {\ifcsname#1\endcsname\else + \resetusage{#1}% + \fi + \csname#1\endcsname} + +%D Very handy, more efficient than \type{{}}, and more readable +%D than \type {\empty}. + +\let\donothing\empty + +% The following macros are used in XML handling. + +\long\setvalue{@u@s@"}#1#2"{#2} \long\setvalue{@g@s@"}#1#2"{\scratchtoks{#2}} +\long\setvalue{@u@s@'}#1#2'{#2} \long\setvalue{@g@s@'}#1#2'{\scratchtoks{#2}} +\long\setvalue{@u@s@ }#1#2 {#2} \long\setvalue{@g@s@ }#1#2 {\scratchtoks{#2}} + +\long\def\unstringed#1{\csname\ifcsname @u@s@#1\endcsname @u@s@#1\else\s!empty\fi\endcsname#1} +\long\def\grabstring#1{\csname\ifcsname @g@s@#1\endcsname @g@s@#1\else\s!empty\fi\endcsname#1} + +\def\dowithgrabbedstring#1% + {\def\@@dowithgrabbedstring{#1}% + \afterassignment\@@dowithgrabbedstring\grabstring} + +\def\expifequalelse#1#2% + {\@@ifequal#1\relax\relax\@@and#2\relax\relax\@@then} + +\def\@@ifequal#1#2\@@and#3% + {\ifx#1\relax + \ifx#3\relax + \@EAEAEA\@@if@@equal@@true + \else + \@EAEAEA\@@if@@equal@@false + \fi + \else + \ifx#3\relax + \@EAEAEAEAEAEA\@@if@@equal@@false + \else\ifx#1#3% + % go on + \else + \@EAEAEAEAEAEA\@@if@@equal@@false + \fi\fi + \fi + \@@ifequal#2\@@and} + +\long\def\@@if@@equal@@true #1\@@then#2#3{#2} +\long\def\@@if@@equal@@false#1\@@then#2#3{#3} + +%D new stuff : + +\def\partialexpanded#1% + {\let\@@notexpanded\noexpand + \long\xdef\@@expanded{\noexpand#1}% + \let\@@notexpanded\empty + \@@expanded} + +\def\appended#1#2#3{\@EA#1\@EA#2\@EA{#2#3}} +\def\appendvalue #1{\@EA\appended\@EA \def\csname#1\endcsname} +\def\appendgvalue#1{\@EA\appended\@EA\gdef\csname#1\endcsname} + +\def\prepended#1#2#3{\scratchtoks{#3}\@EA\@EA\@EA#1\@EA\@EA\@EA#2\@EA\@EA\@EA{\@EA\the\@EA\scratchtoks#2}} +\def\prependvalue #1{\@EA\prepended\@EA \def\csname#1\endcsname} +\def\prependgvalue#1{\@EA\prepended\@EA\gdef\csname#1\endcsname} + +%D \macros +%D {compresscommacommandnrs,compresscommalistnrs,compressedcommalistnrs, +%D compresscommacommand,compresscommalist,compressedcommalist, +%D reversecommacommand,reversecommalist,reversedcommalist} +%D +%D The following two list processing macros are needed by Taco's +%D bibliography module. The numbers compressor converts the +%D list in a list of ranges. The normal compressor remove duplicate +%D and empty entries. + +\def\compresscommalistnrs[#1]% + {\let\compressedlist\empty + \!!counta\maxdimen + \!!countb\maxdimen + \processcommalist[#1]\docompresslistnrs + \ifnum\!!counta=\maxdimen\else\dodocompresslistnrs\fi} + +\def\compresscommacommandnrs[#1]% + {\normalexpanded{\noexpand\compresscommalistnrs[#1]}} + +\def\docompresslistnrs#1% + {\edef\commalistelement{#1}% + \ifx\commalistelement\empty\else + \ifnum\!!counta=\maxdimen + \!!counta\commalistelement\relax + \!!countb\!!counta + \else + \advance\!!countb\plusone + \ifnum\commalistelement>\!!countb + \advance\!!countb\minusone + \dodocompresslistnrs + \!!counta\commalistelement\relax + \!!countb\!!counta + \fi + \fi + \fi} + +\def\dodocompresslistnrs + {\edef\compressedlist + {\ifx\compressedlist\empty\else\compressedlist,\fi + {\the\!!counta}{\ifnum\!!countb>\!!counta\the\!!countb\fi}}} + +%D \def\test#1{{\tttf#1->\compresscommalistnrs[#1]\defconvertedcommand\ascii\compressedlist\ascii}} +%D \startlines +%D \test{} +%D \test{1} +%D \test{1,3} +%D \test{1,3,4} +%D \test{1,3,3,4,5} +%D \test{1,3,3,4,5,8} +%D \test{1,3,3,4,5,5,8,10} +%D \test{1,3,4,5,8,10,11} +%D \test{1,,3,,4,,5,,8,,10,,11,} +%D \stoplines + +\def\compresscommalist[#1]% + {\let\compressedlist\empty + \let\!!stringa\empty + \processcommalist[#1]\docompresslist} + +\def\compresscommacommand[#1]% + {\normalexpanded{\noexpand\compresscommalist[#1]}} + +\def\docompresslist#1% + {\edef\commalistelement{#1}% + \ifx\commalistelement\empty \else + \ifx\!!stringa\commalistelement \else + \ifx\compressedlist\empty + \def\compressedlist{#1}% + \else + \appended\def\compressedlist{,#1}% + \fi + \let\!!stringa\commalistelement + \fi + \fi} + +%D \def\test#1{{\tttf#1->\compresscommalist[#1]\defconvertedcommand\ascii\compressedlist\ascii}} +%D \startlines +%D \test{} +%D \test{1} +%D \test{1,3} +%D \test{1,3,4} +%D \test{1,3,3,4,5} +%D \test{1,3,3,4,5,8} +%D \test{1,3,3,4,5,5,8,10} +%D \test{1,3,4,5,8,10,11} +%D \test{1,,3,,4,,5,,8,,10,,11,} +%D \stoplines + +\def\reversecommalist[#1]% + {\let\reversedlist\empty + \processcommalist[#1]\doreverselist} + +\def\doreverselist#1% + {\ifx\reversedlist\empty + \def\reversedlist{#1}% + \else + \prepended\def\reversedlist{#1,}% + \fi} + +\def\reversecommacommand[#1]% + {\normalexpanded{\noexpand\reversecommalist[#1]}} + +%D \def\test#1{{\tttf#1->\reversecommalist[#1]\defconvertedcommand\ascii\reversedlist\ascii}} +%D \startlines +%D \test{} +%D \test{1} +%D \test{1,3} +%D \test{1,3,4} +%D \test{1,3,3,4,5} +%D \test{1,3,3,4,5,8} +%D \test{1,3,3,4,5,5,8,10} +%D \test{1,3,4,5,8,10,11} +%D \test{1,,3,,4,,5,,8,,10,,11,} +%D \stoplines + +%D \macros +%D {stripstring} +%D +%D Needed in bookmarks: +%D +%D \starttyping +%D {\sanitizePDFdocencoding test \CONTEXT\ test \to\oeps\stripstring\oeps\tttf[\oeps]} +%D \stoptyping + +\def\stripstring#1% #1 is \cs + {\edef\cs{\ctxlua + {tex.sprint(tex.vrbcatcodes,string.strip(\!!bs\detokenize\expandafter{#1}\!!es))}}} + +%D \macros +%D {dowithrange} +%D +%D This one is for Mojca Miklavec, who made me aware of the fact that +%D \type {page-imp.tex} was not the best place to hide it. + +\def\dowithrange#1#2% #2 takes number + {\splitstring#1\at:\to\fromrange\and\torange + \ifx\torange\empty\let\torange\fromrange\fi + \dostepwiserecurse\fromrange\torange1{#2{\recurselevel}}} + +%D \macros {uncompresslist} +%D +%D When given a list like \type{1,4-7,9} as argument, this macro +%D will store the expanded commalist in \type{\uncompressedlist}. +%D +%D \startbuffer +%D \def\MojcaHasToDoTheTasks[#1]#2% +%D {{\uncompresslist[#1]% +%D \def\processitem##1{I have to do ##1 #2\par}% +%D \processcommacommand[\uncompressedlist]\processitem}} +%D +%D \MojcaHasToDoTheTasks [1-4,7,9-11] {until tomorrow} +%D \stopbuffer +%D +%D Here is an example of how to use \type {\uncompresslist}: +%D \typebuffer +%D +%D The output of this is: +%D +%D \getbuffer + +\def\uncompresslist[#1]% by TH + {\let\uncompressedlist\empty + \def\docompressedlistitem##1-##2-% + {\@EA\dorecurse\@EA + {\the\numexpr1+##2-##1\relax}% + {\@EA\appendtocommalist\@EA{\the\numexpr##1-1+####1\relax}\uncompressedlist}}% + \def\douncompresslist##1% + {\doifinstringelse{-}{##1} + {\docompressedlistitem##1-} + {\appendtocommalist{##1}\uncompressedlist}}% + \processcommalist[#1]\douncompresslist} + +%D \macros +%D {ignoreimplicitspaces} +%D +%D \startbuffer +%D \def\whatever[#1]{\expanded{\definedfont[#1 at 12pt]}\ignorespaces} +%D {a\whatever[Serif]b a\whatever[Serif] b a\whatever[Serif]\space b} +%D \def\whatever[#1]{\expanded{\definedfont[#1 at 12pt]}\ignoreimplicitspaces} +%D {a\whatever[Serif]b a\whatever[Serif] b a\whatever[Serif]\space b} +%D \stopbuffer +%D +%D \typebuffer \getbuffer + +\def\ignoreimplicitspaces + {\doifnextcharelse\relax\relax\relax} + +%D \macros +%D {processwords} +%D +%D Not that sophisticated but sometimes users (like in metafun). + +\def\doprocesswords#1 #2\od + {\doifsomething{#1}{\processword{#1} \doprocesswords#2 \od}} + +\def\processwords#1% + {\doprocesswords#1 \od}% no \unskip + +\let\processword\relax + +% new +% +% \startnointerference +% all kind of code +% \stopnointerference + +\newbox\nointerferencebox + +\def\startnointerference % not even grouped ! + {\setbox\nointerferencebox\vbox + \bgroup} + +\def\stopnointerference + {\egroup + \setbox\nointerferencebox\emptybox} + +% \def\appendtovaluelist#1#2% +% {\ifcsname#1\endcsname +% \expandafter\ifx\csname#1\endcsname\empty +% \expandafter\def\csname#1\endcsname{#2}% +% \else +% \expandafter\def\csname#1\expandafter\expandafter\expandafter\endcsname +% \expandafter\expandafter\expandafter{\csname#1\endcsname,#2}% +% \fi +% \else +% \expandafter\def\csname#1\endcsname{#2}% +% \fi} +% +% or +% +% \def\appendtovaluelist#1% +% {\ifcsname#1\endcsname +% \expandafter\ifx\csname#1\endcsname\empty +% \expandafter\noappendtovaluelist\csname#1\expandafter\expandafter\expandafter\endcsname +% \else +% \expandafter\doappendtovaluelist\csname#1\expandafter\expandafter\expandafter\endcsname +% \fi +% \else +% \expandafter\noappendtovaluelist\csname#1\expandafter\endcsname +% \fi} + +% \def\doappendtovaluelist#1#2{\expandafter\def\expandafter#1\expandafter{#1,#2}} +% \def\noappendtovaluelist#1#2{\def#1{#2}} + +% \appendtovaluelist{mylist}{aap} +% \appendtovaluelist{mylist}{noot} +% \appendtovaluelist{mylist}{mies} + +% \showvalue{mylist} + +%D A variant for \type {\executeifdefined}: + +\def\expandcheckedcsname#1#2#3% + {\csname#1\ifcsname#1#2\endcsname#2\else#3\fi\endcsname} + +\protect \endinput |