
%%%%%%%%%%%%%%%  DATABASE STYLE  %%%%%%%%%%%%%%%%%%
%
% db.sty -- version 2.3
%
% Style to generate listings, to send out letters, etc.  using
% a database.
%
% Version 2.2 (11/22/91)
%
% Written by George Panagopoulos (georgios@umiacs.umd.edu)
%
% Version 2.3 (01/16/93)
% 
% Modified by Jose Alberto Fernandez (alberto@cs.umd.edu)
% 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% If you have never use db.sty you should look first at the simple
% examples presented in the NEXT section, before trying to understand
% the new features.
% 
% CHANGES from version 2.2
% 
% In version 2.3 there are two new features, a new macro to do
% conditional expansion based on the values of fields in the entry,
% and an extended syntax for the conditions that allow conjunctive as
% well as disjunctive selction operators.
% 
% 1) There is a new macro \dbif[<selector-exp>]{then-text}{else-text} that
% can be used to do conditional insertion of text in the generated
% letter. Ex:
% 
% Assume that your database uses a field \position that is set to
% 'academic' or 'industry' depending in the kind of place you are
% sending your application letter. You can add conditional sentences
% in your letter as follows:
% 
% ``. . . I have \dbif[position=academic]{5 year of experience in
%   teaching undergraduate courses . . . .}{work in the development of
%   large scale applications in the area of . . .}. . .''
% 
% 2) In the previous version of db.sty one could only check if a
% particular field was defined or not. Now it is possible to select
% depending on more complicated queries. The general syntax of the
% selector is as follows:
% 
% <selector-exp> := [<selec-and>;. . .;<selec-and>]
% 
% <selec-and>    := <selec-field>,. . .,<selec-field>
% 
% <selec-field>  := <field-name>=<text-value>
% <selec-field>  := <field-name>
% 
% The ``;'' operator represents OR; the ``,'' operator represents AND;
% an expresion using ``='' indicates that the field named must have
% that particular value for the selection to apply; and a field name
% with no ``=<text>'' means that the field must be have some value
% assigned on this case. (A value different than empty). Ex:
% 
%   \dbif[a,b=17;b=27]{text1}{text2}: It will write ``text1'' if the
%   field ``a'' is defined AND the field ``b'' expands to the text
%   ``17'' OR if ``b'' expand to ``27'' no matter what is the value of
%   ``a''. 
%   
%   \db{file1}[letter={to be sent}]: It will only consider those
%   tuples from the database file ``file1.tdb'' that define the field
%   ``letter'' to expend to the text ``to be sent''.
%
%
%%%%%%%%%%%%  SAMPLE DATABASE FILE mydb.tdb:
%   
%   % Schema definition
%   
%   \fields{FName, LName, 
%           Email, Addr, 
%           local} 
%   
%   % Database
%   
%   \tuple{FName=	George,
%          LName=	Panagopoulos,
%          Addr=	{CS Dept., U of MD, College Park 20742},
%          Email=	{\tt georgios@umiacs.umd.edu},
%          local=	1}
%   
%   \tuple{FName=	Somebody,
%          LName=	Else,
%          Email=	{\tt sb@elsewhere}} % all other fields empty
%   
%   \tuple{FName=	Ten,
%          LName=	\FirstName}
%   
%%%%%%%%%%%%%%  SAMPLE USE FILE mydb.tex:
%
% \documentstyle[db]{article}
%   
% \begin{document}
%   
% % List 1
%   
% \newcommand{\tupletext}{\thetuplectr & \FName\ \LName & \Email \\} 
% \begin{tabular}{rll}
%   \db{mydb} % Lists all
% \end{tabular}
% ===
% \thetuplectr\ person(s) listed
%  
% % List 2
%   
% \renewcommand{\tupletext}{\FName\ (\Email)} 
% \ref{num:tuples} local person(s):
%   
% \begin{enumerate}
%   \db{mydb}[local] % lists all that have nonempty \local
%   \label{num:tuples} 
% \end{enumerate}
%   
% \end{document}
%
%%%%%%%%%%%%%%  OUTPUT of mydb.tex:
%
% 1 George Panagopoulos  georgios@umiacs.umd.edu
% 2 Somebody Else        sb@elsewhere
% 3 Ten Ten
% ===
% 3 person(s) listed
% 
% 1 person(s) below:
% 
% 1. George (georgios@umiacs.umd.edu)
%
%%%%%%%%%%%%%%% NOTES (refer to example above)
%  
% Database files have the extension ".tdb".
%  
% Field order is irrelevant.
%  
% A subset of the fields may be given as argument in the \tuple command.
% The rest become empty.
%  
% TeX/LaTeX commands are valid inside field values.
%  
% Other fields are valid inside field values. 
%  
% A field value must not contain a comma, unless the comma is enclosed
% in braces.
%                       
% White space is insignificant outside \fields and \tuple commands in
% the database file.
%  
% White space is insignificant inside a \fields or a \tuple command,
% except between the first letter of a field name and the following
% equal sign, and between the first letter of a field value and the
% following comma or closing brace.  
% Therefore: 
%   In a \fields command, *there must be no space between the field name
%     and the following comma or closing brace*: 
%       \fields{ Name, Addr} 
%     is valid, but
%       \fields{Name ,Addr} 
%     is invalid, and so is 
%       \fields{Name,Addr } 
%   In a \tuple command, *there must be no space between the field name
%     and the equal sign, and no unwanted spaces between the field value
%     and the following comma or closing brace*:
%       \tuple{ Name= x xx , Addr= yy y } 
%     is equivalent to
%       \tuple{Name={x xx },Addr={yy y }}, 
%     but
%       \tuple{Name =xxx,Addr=yyy} 
%     is invalid.
% 
% Beware of conflicts between field names and other TeX macros. The 
% \fields command issues a warning if your field name conflicts with an
% already defined macro, but goes on to redefine it anyway.
% 
% The field macros become undefined after the execution of the the \db
% command. The old values (if applicable) are NOT restored.
% 
% The same database and more than one databases may be included in the
% same document.
% 
% The counter {tuplectr} is used to count tuples. The macro \thetuplectr
% may be used and/or modified at wish. The value of the counter is saved
% in the \ref variable in the end of the \db command. 
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%% MODIFIED \@for LOOP CONSTRUCT

% LaTeX's @for expands the list (with \edef\@fortmp{...}), which is
% undesirable, since it tries to expand the field value, which may not
% make sense until inside the environment that encloses \db. 
% Therefore \@foreach is used instead during tuple parsing.
% \@foreach does not work for empty lists.

\def\@foreach#1:=#2\do#3{\expandafter\@forloop#2,\@nil,\@nil\@@#1{#3}}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\newcounter{tuplectr}

%%%%%%%%%%%% USEFUL MACROS

\def\ignorewhitespace{\catcode`\ =9\catcode`\^^M=9}
\def\noignorewhitespace{\catcode`\ =10\catcode`\^^M=5}

\ignorewhitespace

\long\def\db@ifb#1{ % trim one space from #1, if needed
  \ifx \space #1
     \else #1 \fi}

%%%%%%%%%%%% SCHEMA READING

\def\chk@fld@name#1{ % check fldname #1 for conflict with other macros
   \expandafter\ifx \expandafter \relax \csname#1\endcsname % if undefined
     \else
       \expandafter\let\csname#1\endcsname\relax % undefine macro
       \typeout{Warning: \space field \space name \space 
                \expandafter\string\csname#1\endcsname 
                \space redefines \space a \space TeX \space macro} 
     \fi}

\def\@field#1{ % expand \init@fields with: \def\#1{} (#1=fldname)
               % and \undef@fields with: \let\#1\relax
   \chk@fld@name{#1}
   \edef\init@fields{
       \init@fields
       \expandafter\def\csname#1\endcsname{}}
   \edef\undef@fields{
       \undef@fields
       \expandafter\let\csname#1\endcsname\relax}}

\def\parse@fld@names#1{ % set \init@fields to clear all fields
                        % and \undef@fields to undef all field macros
   \@for\fld@name:=#1
       \do {\@field{\fld@name}}}

\def\fields#1{ % read field names (keeps ignoring whitespace)
   \edef\init@fields{}
   \edef\undef@fields{}
   \parse@fld@names{#1}}

%%%%%%%%%%%% TUPLE PARSING

\long\def\@setfield#1=#2,{
     \expandafter\def\csname\db@ifb#1\endcsname{#2}}

\long\def\parse@fields#1{
   \@foreach\@args:=#1
       \do {\expandafter\@setfield\@args,}}

%%%%%%%%%%%% TUPLE SELECTION
\newif \ifdb@test

\def\makedb@test#1=#2=#3,{ % test if condition satisfied
                           % #1: Field, #2: Selection value, #3: nouse
   \bgroup% Only macros defined here can be global
      \let\\=\relax% Hack for multiline fields
      \@ifundefined{#1}{\gdef\db@testval{}%
      }{%
      \xdef\db@testval{\csname#1\endcsname}}% eval field
      \xdef\db@select{#2}% get selection value
   \egroup%
%   \typeout{\db@testval = \db@select}%
   \ifx \empty \db@select % ignore tuple if \db@testval=\empty
      \ifx \db@testval \empty%
            \db@testfalse%
      \fi%
    \else% ignore tuple if \db@testval=/=\db@select
      \ifx \db@testval \db@select%
        \else \db@testfalse%
      \fi%
   \fi}

\long\def\db@and#1#2#3{% If #1 is satisfied then #2 else #3
%   \typeout{[#1]}%
   \db@testtrue%
   \@foreach\db@test:=#1%
      \do{\expandafter\makedb@test\db@test==,}%
   \ifdb@test #2\else #3\fi%
}

\long\def\db@or[#1;#2]#3{% #1 OR #2 then #3 else #4
   \ifx #1\empty%
     \else%
        \db@and{#1}{#3}{%
           \expandafter\db@or\expandafter[#2]{#3}}%
   \fi
}

\long\def\dbif[#1]#2#3{% #1 disjunction separated by commas
 \begingroup%
   \def\db@else{#3}
   \db@or[#1;;]{#2\let\db@else\relax}% Add ;; to mark end-of-list
   \db@else
 \endgroup%
}
\let\dbexpr\dbif % Old syntax for conditionals.

%%%%%%%%%%% TUPLE WRITING

\def\print@tuple{ % print tuple value as requested
   \ifx \empty \db@expr % always print
            \refstepcounter{tuplectr}
            \tupletext
    \else % evaluate expr fileds
       \expandafter\dbif\expandafter[\db@expr]{
          \refstepcounter{tuplectr}
          \tupletext
       }{}
    \fi}

%%%%%%%%%%%% TUPLE READING

\long\def\@tuple#1{ % parse tuple and call \print@tuple
   \init@fields % empty field values
   \parse@fields{#1}
   \print@tuple
   \ignorewhitespace}

\def\tuple{ % read tuple, print \tupletext if \db@flag not empty
   \noignorewhitespace % will be restored by \@tuple
   \@tuple}

\long\def\notuple#1{} % ignore entry

%%%%%%%%%%%% DB READING

\def\@db#1[#2]{ % #1=file, #2=fields to be tested
  \def\db@expr{#2} 
  \setcounter{tuplectr}{0}
  \ignorewhitespace
  \@input{#1.tdb}
  \undef@fields
  \noignorewhitespace}

\def\db#1{ % read database, print \tupletext for each included tuple
  \@ifnextchar [
      {\@db#1} 
      {\@db#1[]}}

\noignorewhitespace


-- 
: Jose Alberto Fernandez R   \    |    /    INTERNET: alberto@cs.umd.edu :
:   Dept. of Computer Sc.     \   |   /   BITNET: alberto@cs.umd.edu     :
:     University of Maryland   \  |  /  PHONE: (301) 405-2716            :
:       College Park, MD 20742  \ | / FAX/HOME: (202) 265-5784           :


