
%%%%%%%%%%%%%%%  DATABASE STYLE  %%%%%%%%%%%%%%%%%%

% db.sty
%
% Style to generate listings, to send out letters, etc.  using
% a database.
%
% Version 2.1 (11/20/91)
%
% Written by George Panagopoulos (georgios@umiacs.umd.edu)

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

\long\def\DOCCOMMENT#1\ENDDOCCOMMENT{}

\DOCCOMMENT

%%%%%%%%%%%%  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. 

\ENDDOCCOMMENT

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

\newcounter{tuplectr}

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

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

\ignorewhitespace

\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 \csname#1\endcsname \relax % if undefined
     \else
       \global\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: \gdef\#1{} (#1=fldname)
                % and \undef@fields with: \global\let\#1\relax
   \edef\fld@name{\db@ifb#1}
   \chk@fld@name{\fld@name}
   \xdef\init@fields{
       \init@fields
       \expandafter\gdef\csname\fld@name\endcsname{}}
   \xdef\undef@fields{
       \undef@fields
       \global\expandafter\let\csname\fld@name\endcsname\relax}}

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

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

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

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

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

\def\print@tuple{ % print tuple value as requested
   % \db@flag is not expanded and contains the name of a field
   \edef\db@flag@val{\db@flag} % eval \db@flag
   \ifx \db@flag@val \empty % ignore tuple if \db@flag=\empty
      \else 
         \refstepcounter{tuplectr}
         \tupletext
   \fi}

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

\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}

\def\notuple#1{} % ignore entry

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

\def\@db#1[#2]{ % #1=file, #2=field not to be empty for \tupletext
  \ifx . #2
       \gdef\db@flag{1} % always nonempty
     \else
       \xdef\db@flag{\csname#2\endcsname}
     \fi
  \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
