%D \module %D [ file=syst-new, %D version=1997.01.03, %D title=\CONTEXT\ Support Macros, %D subtitle=New Ones, %D author=Hans Hagen, %D date=\currentdate, %D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] %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 Code from this module will move. For instance to %D syst-dat.tex (datastructures). \unprotect % 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 {\ifundefined{#1}\@EA\let\csname#1\endcsname\empty\fi \expanded{\addtocommalist{#2}\@EA\noexpand\csname#1\endcsname}} % faster, and looks okay \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% {\defconvertedargument#2{#1}% \@EA\edef\@EA#2\@EA{\@EA\unspaced\@EA{#2}}} \def\unspaceafter#1#2% {\edef\ascii{\dounspaced#2\end}\@EA#1\@EA{\ascii}} % etex, much faster % % \def\unspaceargument#1\to#2% % {\scratchcounter\catcode'32\catcode32=\@@ignore % \scantokens{\edef#2{#1}}% % \catcode32=\scratchcounter} \def\unspaceafter#1#2% {\edef\ascii{\dounspaced#2\end}\@EA#1\@EA{\ascii}} % sometimes handy: \def\doifhasspaceelse#1% {\edef\!!stringa{#1}% \expanded{\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\chardef\csname\@flg@#1\endcsname\zerocount} \def\resetflag#1% {\@EA\dodoglobal\@EA\chardef\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] {\@EAEAEA\def\@EA\csname\@EA#1\@EA#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\doifnonzeropositiveelse#1#2#3% % {\bgroup % \setbox\scratchbox=\hbox{\scratchcounter=0#1}% % \ifdim\wd\scratchbox=\!!zeropoint\egroup#2\else\egroup#3\fi} % % \def\dodoifnonzeropositiveelse#1#2\end#3#4% % {\egroup % \ifx#1\relax#3\else#4\fi} % % \def\doifnonzeropositiveelse#1% % {\bgroup % \afterassignment\dodoifnonzeropositiveelse\scratchcounter=0#1\relax\empty\end} \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\zeropoint \!!plus \onepoint \!!minus \onepoint \advance\scratchskip#1\relax % \relax is realy needed here \expandafter\SPLITSKIP\the\scratchskip} {\catcode`\.=\@@other \catcode`\p=\@@other \catcode`\l=\@@other \catcode`\u=\@@other \catcode`\s=\@@other \catcode`\m=\@@other \catcode`\i=\@@other \catcode`\n=\@@other \catcode`\t=\@@other \gdef\SPLITSKIP#1pt plus #2pt minus #3pt% {\DOSPLITSKIP#1 #2 #3 }} % \def\DOSPLITSKIP#1 #2 #3 % {\dimen0=#1pt\dimen2=#2pt\dimen4=#3pt % \advance\dimen2 -\onepoint % \advance\dimen4 -\onepoint} \def\DOSPLITSKIP#1 #2 #3 {\dimen0=#1\onepoint \dimen2=\dimexpr#2\onepoint-\onepoint\relax \dimen4=\dimexpr#3\onepoint-\onepoint\relax} % \def\minimaxskip#1#2% % {\splitskip#2\relax % \scratchdimen=#2\relax % #2=\scratchdimen % \advance#2 by #1\relax} % % \def\maximizeskip% % {\minimaxskip{-\dimen4}} % % \def\maximizeskip% % {\minimaxskip{\dimen2}} % % \def\maximizespacing% % {\maximizeskip\blankskipamount % \maximizeskip\parskip % \maximizeskip\ctxparskip % \maximizeskip\baselineskip % \maximizeskip\bigskipamount % \maximizeskip\medskipamount % \maximizeskip\smallskipamount} \newcount\modcounter %\def\DoMod #1by#2to#3% % {\modcounter#1\divide\modcounter#2\multiply\modcounter#2% % #3#1\advance#3 -\modcounter} % %\def\DoDiv #1by#2to#3% % {#3#1\divide#3 #2\relax} \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} %D Standaard kan een spatie (zoals ~) uitrekken. Dit is in %D overzichten niet altijd de bedoeling, vandaar: % \def\fixedspace{\hskip\interwordspace\relax} %\def\ExpandSecondAfter#1#2#3% % {\!!toksa={#2}% % \edef\!!stringa{#3}% % \edef\expanded% % {\noexpand#1{\the\!!toksa}{\!!stringa}}% % \expanded} % %\def\ExpandThirdAfter#1#2#3#4% % {\!!toksa={#2}% % \!!toksb={#3}% % \edef\!!stringa{#4}% % \edef\expanded% % {\noexpand#1{\the\!!toksa}{\the\!!toksb}{\!!stringa}}% % \expanded} %\def\indirect#1#2#3% % {\@EA#1\@EA#2\@EA{\@EA#3\csname\s!do\string#2\endcsname}% % \@EA#1\csname\s!do\string#2\endcsname} % %\def\doubleemptied#1#2#3% % {\indirect#1#2\dodoublempty} % %\indirect\def\stelietsin\dodoubleempty[#1][#2]% % {...} % %\doubleemptied\def\stelietsin[#1][#2]% % {...} % in mult-set % %\def\defaultsetup{def} % %\def\selectdefaultsetup#1#2% % {\writestatus{setup}{choose #1 setupfile}% % \bgroup % \endlinechar=-1 % \global\read16 to \usersetup % \egroup % \ifx\usersetup\empty % \let\usersetup=\defaultsetup % \fi % \readfile{#2\usersetup}{}{}% % \writestatus{setup}{loading #1 setupfile #2\usersetup}} % awaiting the definitive implementation \let\normalelapsedtime\elapsedtime \ifx\resettimer\undefined \let\resettimer \relax \newcount\elapsedtime \fi \newcount\featuretest \newcount\noffeaturetest \newcount\featuretesttime \def\testfeature#1#2% brought in sync with mkiv {\noffeaturetest#1\relax \def\dotestfeature {\advance\featuretest\plusone \ifnum\featuretest>\noffeaturetest\else#2\expandafter\dotestfeature\fi}% \def\notestfeature {\advance\featuretest\plusone \ifnum\featuretest>\noffeaturetest\else\expandafter\notestfeature\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 \bgroup \featuretest\zerocount \notestfeature \global\featuretesttime\normalelapsedtime \egroup \resettimer \bgroup \featuretest\zerocount \dotestfeature \egroup \global\featuretesttime\numexpr\normalelapsedtime-\featuretesttime\relax \writestatus\m!systems{feature test done (used: \elapsedseconds s)}% \wait \egroup} \def\elapsedseconds{\expandafter\withoutpt\the\dimexpr\featuretesttime sp\relax} \let\elapsedtime\elapsedseconds \def\showtimer#1% {\writestatus{runtime}{\elapsedseconds\space s / #1}} % \edef\elapsedtime{\expandafter\withoutpt\the\dimexpr\elapsedtime\relax s}% % \writestatus\m!systems{feature test done (\elapsedtime)}% \def\testfeatureonce#1#2% {\let\wait\relax\testfeature{#1}{#2}\end} %D \macros %D {adddimenregister,adddimenmacro} %D %D Instead of using numerous \type {\advance}'s, one can use %D the next macros to add|/|subtract a series of dimensions %D to a register or macro. %D %D \starttyping %D \adddimenregister 10pt 5pt \paperwidth \to \somedimen %D \adddimenmacro 10pt 5pt \paperwidth \to \bagger %D \stoptyping % \newdimen\dimentoaddto % \def\adddimenregister#1\to#2% will be obsolete once full etex % {\begingroup % #2=\zeropoint % \dimentoaddto\zeropoint % \def\docommand% % {\advance#2 \dimentoaddto % \futurelet\next\dodocommand}% % \def\dodocommand% % {\ifx\next\relax % \expanded{\endgroup#2=\the#2}% % \else % \@EA\afterassignment\@EA\docommand\@EA\dimentoaddto % \fi}% % \docommand#1\relax} % \def\adddimenmacro#1\to#2% % {\adddimenregister#1\to\scratchdimen % \edef#2{\the\scratchdimen}} %D \macros %D {freezedimenmacro} %D %D This macro is use as: %D %D \starttyping %D \freezedimenmacro\leftmargindistance %D \stoptyping % \def\freezedimenmacro#1% % {\scratchdimen#1\edef#1{\the\scratchdimen}} \def\freezedimenmacro#1% {\edef#1{\the\dimexpr#1}} %D The next one is slower: %D %D \starttyping %D \def\freezedimenmacro#1{\edef#1{\the\dimexpr(#1)}} %D \stoptyping % \newcount\rawrecursecounter % % \def\rawrecurselevel{\the\rawrecursecounter}% % % \def\dorawrecurse#1#2% % {\rawrecursecounter\plusone % \let\oldrecurselevel\recurselevel % \let\recurselevel\rawrecurselevel % \def\dodorawrecurse % {\ifnum\rawrecursecounter>#1\relax % \let\recurselevel\oldrecurselevel % \else % #2\advance\rawrecursecounter \plusone % \expandafter\dodorawrecurse % \fi}% % \dodorawrecurse} %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} \beginTEX \def\ifusage#1% {\@EA\ifx\csname#1\endcsname\relax \resetusage{#1}% \fi \csname#1\endcsname} \endTEX \beginETEX \ifcsname \def\ifusage#1% {\ifcsname#1\endcsname\else \resetusage{#1}% \fi \csname#1\endcsname} \endETEX %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\def\dowithstringed#1#2#3#4% " ' space % {\if#4"\@EA#1\else\if#4'\@EAEAEA#2\else\@EAEAEA#3\fi\fi#4} % % \def\unstringed % {\dowithstringed\unstringdouble\unstringsingle\unstringspaced} % % \long\def\unstringdouble"#1"{#1} % \long\def\unstringsingle'#1'{#1} % \long\def\unstringspaced #1 {#1} % % \def\grabstring % {\dowithstringed\grabstringdouble\grabstringsingle\grabstringspaced} % % \def\dowithgrabbedstring#1% % {\def\@@dowithgrabbedstring{#1}% % \afterassignment\@@dowithgrabbedstring\grabstring} % % \long\def\grabstringdouble"#1"{\scratchtoks{#1}} % \long\def\grabstringsingle'#1'{\scratchtoks{#1}} % \long\def\grabstringspaced #1 {\scratchtoks{#1}} \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} % for Simon ; watch how we make them fully expandable (i.e. % no \type {\relax}es) popping up \def\@do@the@number@#1\relax#2\end{#1} \def\@the@number@#1{\@EA\@do@the@number@\number#1\relax\end} \def\doifnum#1#2% {\ifnum#1=\@the@number@{#2} % space needed \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \def\doifnotnum#1#2% {\ifnum#1=\@the@number@{#2} % space needed \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \def\doifelsenum#1#2% {\ifnum#1=\@the@number@{#2} % space needed \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} % \edef\xxxx{\doifnum{1}{2}{no}} \show \xxxx % \edef\xxxx{\doifnum{2}{2}{yes}} \show \xxxx % \scratchcounter0 \edef\xxxx{\doifnum{1}{\scratchcounter}{no}} \show \xxxx % \scratchcounter1 \edef\xxxx{\doifnum{1}{\scratchcounter}{yes}} \show \xxxx \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]% {\expanded{\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]% {\expanded{\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]% {\expanded{\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 {\bgroup \defconvertedcommand\ascii{#1}% \global\let\globalascii\empty \donefalse \expandafter\handletokens\ascii\with\dostripstring \egroup \let#1\globalascii} \def\dostripstring#1% {\ifx#1\blankspace \donetrue \else \ifdone\ifx\globalascii\empty\else \xdef\globalascii{\globalascii\space}% \donefalse \fi\fi \xdef\globalascii{\globalascii#1}% \fi} %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 \beginETEX \numexpr \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} \endETEX \beginTEX \def\uncompresslist[#1]% {\def\uncompressedlist{#1}} \endTEX %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} % new % % \startnointerference % all kind of code % \stopnointerference \newbox\nointerferencebox \def\startnointerference {\setbox\nointerferencebox\vbox \bgroup} \def\stopnointerference {\egroup \setbox\nointerferencebox\emptybox} \protect \endinput % \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}