%D \module
%D   [       file=core-dat,                 % was core-02a,
%D        version=1999.08.10,               % 1997.03.31,
%D          title=\CONTEXT\ Core Macros,
%D       subtitle=Database Support,         % 2A 
%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.

\writestatus{loading}{Context Database Support}

\startmessages  dutch  library: databases
  title: database
      1: --
      2: lokaal bestand --
      3: globaal bestand --
      4: onbekend bestand --
\stopmessages

\startmessages  english  library: databases
  title: databases
      1: --
      2: local file --
      3: global file --
      4: unknown file --
\stopmessages

\startmessages  german  library: databases
  title: Datenbank
      1: --
      2: lokale Datei --
      3: globale Datei --
      4: unbekannte Datei --
\stopmessages

\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 reassing 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!commando=\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{\global\letvalue{\??db:#1}\empty}
\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\docommando##1%
     {\resetrecordentry{##1}%
      \appendtoks\resetrecordentry{##1}\to\resetrecordlist}%
   \processcommacommand[\getvalue{\??db#1\c!velden}]\docommando
   \let\setrecordentry\skiprecord
   \the\resetrecordlist
   \doifelse{#2}{\v!alles} % 't Is nu eenmaal alles 
     {\recordoktrue}
     {\doifelsenothing{#2} % of niets 
        {\recordoktrue}
        {\recordokfalse}}% % zullen we maar zeggen. 
   \ifrecordok
     \let\askedrecords\v!alles
   \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!commando}{##1}}%
   \showmessage{\m!databases}{1}{\askedrecords}%
   \def\doprocessrecords##1%
     {\readjobfile{##1}
        {\showmessage{\m!databases}{2}{(job)}}
        {\readsysfile{##1}
           {\showmessage{\m!databases}{3}{(sys)}}
           {\showmessage{\m!databases}{4}{}}}}%
   \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 mechaism, the current implementation is
%D prefered out of speed reasons. 

\protect

\endinput
