%D \module %D [ file=m-database, %D version=2006.04.23, %D title=\CONTEXT\ Modules, %D subtitle=Database Thingies, %D author=Hans Hagen\& Taco Hoekwater, %D date=\currentdate, %D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] %C This module is part of the \CONTEXT\ macro||package and is %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. \unprotect % % % to be added to mult-* files % % % % todo: \dontcollectseparatedlist via k/v \definesystemvariable{ls} \def\c!first {first} \def\c!last {last} \def\c!quotechar {quotechar} \let\@NX\noexpand %D {processquotedlist} %D %D An even more general list processing macro is the %D following one: %D %D \starttyping %D \processquotedlist{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 \processquotedlist(){,}{"}\docommand(a=>b=>c=>d) %D \stoptyping \def\processquotedlist#1#2#3#4#5% {\def\csvquotechar{#4}% \edef\doconvertcsvquoteditem#4##1#4##2% {\@NX\ifx##2#3% \let\@NX\next\@NX\doconvertcsvlist \def\@NX\arg{#3}% \edef\@NX\temp{##1}% \@NX\expanded{\@NX#5{\@NX\temp}}% \@NX\else \let\@NX\next\@NX\redoconvertcsvquoteditem \def\@NX\arg{##1\@NX\csvquotechar}% \@NX\fi \@NX\expandafter\@NX\next\@NX\arg}% \edef\redoconvertcsvquoteditem##1#4##2% {\@NX\ifx##2#3% \let\@NX\next\@NX\doconvertcsvlist \def\@NX\arg{#3}% \edef\@NX\temp{##1}% \@NX\expanded{\@NX#5{\@NX\temp}}% \@NX\else \let\@NX\next\@NX\redoconvertcsvquoteditem \def\@NX\arg{##1\@NX\csvquotechar}% \@NX\fi \@NX\expandafter\@NX\next\@NX\arg}% \edef\doconvertcsvitem##1#3% {\edef\@NX\temp{##1}% \@NX\expanded{\@NX#5{\@NX\temp}}% \@NX\doconvertcsvlist#3}% \edef\doconvertcsvlist#3##1% {\@NX\ifx##1\@NX#2% \let\@NX\next \@NX\gobbleoneargument \@NX\else\@NX\ifx##1#4% \let\@NX\next \@NX\doconvertcsvquoteditem \@NX\else \let\@NX\next \@NX\doconvertcsvitem \@NX\fi\@NX\fi \@NX\next##1}% \doconvertcsvitem} \gdef\doprocessseparatedquoteditem#1% {\appendseparatedlistparameter\c!left% \appendseparatedlistcontent{#1}% \appendseparatedlistparameter\c!right} % a version more robust with regard to {a a} b c d situations: \edef\detokenizedrelax{\detokenize{\relax}} \edef\processseplistseparator{\detokenize{,}}% \def\dodefineprocessseplist#1#2% separator \docommand {\edef\processseplistseparator{\detokenize{#1}}% \def\dodoprocessseplist##1##2#1% {\edef\!!stringa{\detokenize{##1}}% \ifx\detokenizedrelax\!!stringa \expandafter\nodoprocessseplist % \else\ifx\!!stringa#1% \else\ifx\!!stringa\processseplistseparator #2{}% #2{##2}% \expandafter\expandafter\expandafter\dodoprocessseplist \else #2{##1##2}% \expandafter\expandafter\expandafter\dodoprocessseplist \fi\fi}% \def\doprocessseplist##1\relax {\dodoprocessseplist##1#1\relax#1\relax\relax\end}} \def\nodoprocessseplist#1\end {} \long\def\processseplist#1#2#3\relax raw version {\dodefineprocessseplist{#1}{#2}% \dodoprocessseplist#3#1\relax#1\relax\relax\end} % \dodefineprocessseplist{,}\test % \dodoprocessseplist{,}a,b,c\relax,\relax\relax\end % \doprocessseplista,b,c\relax % \def\test#1{[#1]} % \startlines % \processseplist{,}\test ,2,,\relax % \processseplist{,}\test ,,,44\relax % \processseplist{,}\test ,,33,44\relax % \processseplist{,}\test 11,,33,44\relax % \processseplist{,}\test 1,2,3,4\relax % \stoplines \newtoks\separatedlistdata \def\appendseparatedlistparameter#1% {\@EAEAEA\appendtoks\csname\??ls\currentseparatedlist#1\endcsname\to\separatedlistdata} \def\appendseparatedlistcontent#1% {\appendtoks#1\to\separatedlistdata} \def\flushseparatedlistdata {\the\separatedlistdata \separatedlistdata\emptytoks} \def\initializeseparatedlistdata {\separatedlistdata{\egroup}} \def\dontcollectseparatedlist {\def\dodoprocessseparatedfileline {\the\separatedlistdata \separatedlistdata\emptytoks \doprocessseparatedfileline}% \def\dodoprocessseparatedline {\the\separatedlistdata \separatedlistdata\emptytoks \doprocessseparatedline}% \let\flushseparatedlistdata\egroup \let\initializeseparatedlistdata\donothing} \chardef\separatedlistmode\zerocount \def\setcurrentlistseparator {\edef\currentlistseparator {\executeifdefined {\??ls::\csname\??ls\currentseparatedlist\c!separator\endcsname}% {\csname\??ls\currentseparatedlist\c!separator\endcsname}}% \doifvalue{\??ls\currentseparatedlist\c!separator}{tab} {\catcode`\^^I=12\relax}% \ifx\currentlistseparator\empty\def\currentlistseparator{,}\fi} \bgroup \catcode`\^^I=12 \setgvalue{\??ls::tab}{ } \setgvalue{\??ls::space}{ } \setgvalue{\??ls::comma}{,} \egroup \def\doprocessseparatedfileline {\ifeof\scratchread \ifcase\separatedlistmode\appendseparatedlistparameter\c!after\fi \immediate\closein\scratchread \expandafter\flushseparatedlistdata \else\ifx\line\empty % skip, can be comment \read\scratchread to\line \@EA\dodoprocessseparatedfileline \else \appendseparatedlistparameter{\ifcase\separatedlistmode\c!first\else\c!command\fi}% \ifx\currentlistquotechar\empty% \expandafter\doprocessseplist\line\relax \else \expanded{\processquotedlist{}{\noexpand\end}% {\currentlistseparator}{\currentlistquotechar}% \noexpand\doprocessseparatedquoteditem \line\currentlistseparator\noexpand\end}% \fi \ifcase\separatedlistmode\appendseparatedlistparameter\c!last\fi \read\scratchread to\line \@EAEAEA\dodoprocessseparatedfileline \fi\fi} \def\dodoprocessseparatedfileline {\doprocessseparatedfileline} \def\doprocessseparatedfile[#1][#2]% {\bgroup \edef\currentseparatedlist{#1}% \doifdefined{\??ls\currentseparatedlist\c!command}{\chardef\separatedlistmode\plusone}% \setcurrentlistseparator \edef\currentlistquotechar{\csname\??ls\currentseparatedlist\c!quotechar\endcsname}% \expandafter\dodefineprocessseplist\expandafter{\currentlistseparator}\doprocessseparateditem \initializeseparatedlistdata \directsetup{\currentseparatedlist:\executeifdefined{\??ls\currentseparatedlist\c!setups}\s!default}% \ifcase\separatedlistmode\appendseparatedlistparameter\c!before\fi \endlinechar\minusone \ignorelines \catcode`\#\@@comment \immediate\openin\scratchread=#2\relax % todo: \doopenin \read\scratchread to\line \doprocessseparatedfileline} \def\dostartseparatedlist#1[#2]% {\bgroup \edef\currentseparatedlist{#2}% \doifdefined{\??ls\currentseparatedlist\c!command}{\chardef\separatedlistmode\plusone}% \obeylines \let#1\relax \def\separateslistend{#1}% \setcurrentlistseparator \edef\currentlistquotechar{\csname\??ls\currentseparatedlist\c!quotechar\endcsname}% \expandafter\dodefineprocessseplist\expandafter{\currentlistseparator}\doprocessseparateditem \directsetup{\currentseparatedlist:\executeifdefined{\??ls\currentseparatedlist\c!setups}\s!default}% \initializeseparatedlistdata \ifcase\separatedlistmode\appendseparatedlistparameter\c!before\fi \dodostartseparatedlist} \def\redoprocessseparatedline#1% {\def\!!stringa{#1}% \ifx\!!stringa\separateslistend \ifcase\separatedlistmode\appendseparatedlistparameter\c!after\fi \expandafter\flushseparatedlistdata \else% \appendseparatedlistparameter{\ifcase\separatedlistmode\c!first\else\c!command\fi}% \ifx\currentlistquotechar\empty% \doprocessseplist#1\relax \else% \defconvertedargument\csvdata{#1}% \expanded{\processquotedlist{}{\noexpand\end}% {\currentlistseparator}{\currentlistquotechar}% \noexpand\doprocessseparatedquoteditem \csvdata\currentlistseparator\noexpand\end}% \fi \ifcase\separatedlistmode\appendseparatedlistparameter\c!last\fi \expandafter\dodoprocessseparatedline \fi} \def\doprocessseparatedline {\doifnextbgroupelse\xdoprocessseparatedline\ydoprocessseparatedline} \def\dodoprocessseparatedline {\doprocessseparatedline} \def\doprocessseparateditem#1% {\ifcase\separatedlistmode \appendseparatedlistparameter\c!left \appendseparatedlistcontent{#1}% \appendseparatedlistparameter\c!right \else \appendseparatedlistcontent{{#1}}% \fi} \bgroup \obeylines \gdef\dodostartseparatedlist#1 {\doprocessseparatedline} \gdef\xdoprocessseparatedline#1#2 {\redoprocessseparatedline{{{#1}}#2}} \gdef\ydoprocessseparatedline#1 {\redoprocessseparatedline{#1}} \egroup \startsetups CSV:unix \catcode`\#=\@@comment \stopsetups \def\defineseparatedlist {\dodoubleempty\dodefineseparatedlist} \def\dodefineseparatedlist[#1][#2]% {\setvalue{\e!start#1}{\expandafter\dostartseparatedlist\csname\e!stop#1\endcsname[#1]}% \getparameters [\??ls#1] [\c!separator=, \c!quotechar=, \c!first=, \c!left=, \c!before=, \c!right=, \c!last=, \c!after=, % \c!command=, #2]} \def\setupseparatedlist {\dodoubleempty\dosetupseparatedlist} \def\dosetupseparatedlist[#1]% [#2] {\getparameter[\??ls#1]} % [#2] \def\startseparatedlist[#1]% {\dostartseparatedlist\stopseparatedlist[#1]} \def\processseparatedfile {\dodoubleargument\doprocessseparatedfile} \protect \doifnotmode{demo}{\endinput} \defineseparatedlist [CSV] [separator={,}, first=\bTR,last=\eTR, left=\bTD,right=\eTD, before=\bTABLE,after=\eTABLE] \startseparatedlist[CSV] a,b,c d,e,f \stopseparatedlist \startCSV a,b,c d,e,f \stopCSV \defineseparatedlist [CSV] [separator={,}, quotechar={"}, first=\NC,last=\NR, left=,right=\NC, before={\starttabulate[|l|l|l|]},after=\stoptabulate] \startCSV a,b,"c,d" d,"""",f \stopCSV \defineseparatedlist [CSV] [separator={ }, first=\NC,last=\NR, left=,right=\NC, before={\starttabulate[|l|l|l|]},after=\stoptabulate] \startCSV a b c d e f \stopCSV \defineseparatedlist [CSV] [setups=unix, first=\NC,last=\NR, left=,right=\NC, before={\starttabulate[|l|l|l|]},after=\stoptabulate] % \startsetups CSV:unix % \catcode`\#=\@@comment % \stopsetups % %1,2,3 % 1,2,3 % # 4,5,6 % 4,5,6 \processseparatedfile[CSV][test.dat] \defineseparatedlist [CSVX] [command=\Whatever, separator={,}, first=\bTR,last=\eTR, left=\bTD,right=\eTD, before=\bTABLE,after=\eTABLE] \def\Whatever#1#2#3{[#1][#2][#3]\endgraf} \startseparatedlist[CSVX] a,b,c d,e,f \stopseparatedlist \defineseparatedlist[CSV] [separator=comma, before=\bTABLE, after=\eTABLE, first=\bTR, last=\eTR, left=\bTD, right=\eTD] \startCSV a,b,c,č d,e,f,š \stopCSV \enableregime[utf] \defineseparatedlist[X][separator=X,left=(,right=),first=\endgraf,last=\endgraf] \defineseparatedlist[Y][separator=Y,left=(,right=),first=\endgraf,last=\endgraf] \startX aXb Xc čXš \stopX \startY aYb Yc čYš \stopY \stoptext