%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