%D \module %D [ file=m-datastrc, % was: core-dat % was core-02a %D version=1999.08.10, % 1997.03.31, %D title=\CONTEXT\ Modules, %D subtitle=Database Support, % 2A %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. \writestatus{loading}{ConTeXt Core Macros / Database Support} \unprotect %D This module is a (limited) rewrite of the original \type %D {core-02a} module, the module that dealt with managing a %D database of addresses. The principles and methods have not %D changed; they are only generalized. %D %D A database file |<|in most cases such a base is generated %D from another one|>| is structured as follows: %D %D \starttyping %D \startrecord{tag} %D \memberofgroup{grouplist} %D \setrecordentry{name}{...} %D .... %D \stoprecord %D \stoptyping %D %D The interface to such a database is defined as follows: %D %D \starttyping %D \definerecord[class][settings] %D \setuprecord[class][settings] %D \definerecordentry[class][name] %D \stoptyping %D %D and processed by %D %D \starttyping %D \processrecords[file list][tag and/or group list] %D \stoptyping %D %D The actual processing is done by a macro assigned to \type %D {command}: %D %D \starttyping %D \setuprecord[class][command=\DoWithRecord] %D \stoptyping %D %D Given that one can ask for a field with %D %D \starttyping %D \getrecordentry{name} %D \stoptyping %D %D such a command can look like: %D %D \starttyping %D \def\DoWithRecord#1% %D {\startpacked %D \let\\=\quad %D name: \getrecordentry{name}~\getrecordentry{family name}\par %D address: \getrecordentry{postal address}\par %D \stoppacked} %D \stoptyping %D %D The argument passed is the tag. The database can look like: %D %D \starttyping %D \startrecord{hagenj} %D \memberofgroup{a,b} %D \setrecordentry{naam}{Hans} %D \setrecordentry{family name}{Hagen} %D \setrecordentry{postal address}{J. Hagen\\Ridderstraat 29\\Hasselt NL} %D \stoprecord %D %D \startrecord{ottenaf} %D \memberofgroup{a} %D \setrecordentry{name}{Ton} %D \setrecordentry{family name}{Otten} %D \setrecordentry{postal address}{A.F. Otten\\Prinsengracht 17\\Hasselt NL} %D \stoprecord %D \stoptyping %D %D The definition of this database looks like: %D %D \starttyping %D \definerecord[address][command=\DoWithRecord] %D %D \definerecordentry[address][name] %D \definerecordentry[address][family name] %D \definerecordentry[address][postal address] %D \stoptyping %D %D The actual processing is now done by (for instance): %D %D \starttyping %D \processrecords[datafile][hagenj] %D \processrecords[datafile][hagenj,offenaf] %D \processrecords[datafile][all] %D \processrecords[datafile][a] %D \processrecords[datafile][b] %D \stoptyping %D %D Of course one can reassign the command used to handle the %D records in between. % \??kt -> % \??kw -> \def\??db {@@db} \def\c!velden{velden} %\newevery \everyrecord \EveryRecord \def\definerecord {\dodoubleempty\dodefinerecord} \def\dodefinerecord[#1][#2]% {\getparameters [\??db#1] [\c!velden=, \c!command=\gobbleoneargument, #2]} \def\setuprecord {\dodoubleargument\dosetuprecord} \def\dosetuprecord[#1][#2]% {\getparameters[\??db#1][#2]}% \def\definerecordentry[#1][#2]% {\edef\recordentries{\getvalue{\??db#1\c!velden}}% \addtocommalist{#2}\recordentries \letvalue{\??db#1\c!velden}\recordentries} %D Watch out: the entries are defined global! While %D processing a record, no grouping is applied. \def\getrecordentry #1{\getvalue {\??db:#1}} \def\resetrecordentry #1{\letgvalueempty{\??db:#1}} \def\assignrecordentry#1{\setgvalue {\??db:#1}} \long\def\skiprecord#1\stoprecord {\egroup} \newif\ifrecordok \newtoks\resetrecordlist \def\processrecords {\dotripleargument\doprocessrecords} \def\doprocessrecords[#1][#2][#3]% {\bgroup \ifx\\\undefined\let\\\relax\fi \def\docommand##1% {\resetrecordentry{##1}% \appendtoks\resetrecordentry{##1}\to\resetrecordlist}% \processcommacommand[\getvalue{\??db#1\c!velden}]\docommand \let\setrecordentry\skiprecord \the\resetrecordlist \doifelse{#2}\v!all % 't Is nu eenmaal alles \recordoktrue {\doifelsenothing{#2} % of niets \recordoktrue \recordokfalse}% % zullen we maar zeggen. \ifrecordok \let\askedrecords\v!all \else \makerawcommalist[#2]\askedrecords \fi \def\checkrecord##1% {\rawdoifinsetelse{##1}{\askedrecords}{\recordoktrue}{}}% \def\presetrecord##1% {\let\setrecordentry\assignrecordentry \let\memberofgroup\gobbleoneargument \the\resetrecordlist \def\stoprecord{\dostoprecord{##1}}}% \def\memberofgroup##1% {\doifsomething{##1} {\rawprocesscommalist[##1]\checkrecord}% \ifrecordok \presetrecord{##1}% \else \expandafter\skiprecord \fi}% \def\startrecord##1% {\bgroup \ifrecordok \presetrecord{##1}% \else \checkrecord{##1}% \ifrecordok \presetrecord{##1}% \fi \fi}% \def\dostoprecord##1% {\relax \egroup %\the\everyrecord \getvalue{\??db#1\c!command}{##1}}% \showmessage\m!databases1\askedrecords \def\doprocessrecords##1% {\readjobfile{##1} {\showmessage\m!databases2{(job)}} {\readsysfile{##1} {\showmessage\m!databases3{(sys)}} {\showmessage\m!databases4{}}}}% \processcommalist[#3]\doprocessrecords \egroup} %D While writing the original implementation, I did some %D experiments with \type {%} before each entry and changing %D the category code of the comment char. Because \TEX\ scans %D the line anyway |<|this is needed because the end of line %D character can be non standard|>| this is not faster. %D %D Although this mechanism could have been combined with the %D block moving mechanism, the current implementation is %D prefered out of speed reasons. \protect \endinput