From wester@amber.unm.edu Sat Nov 27 00:08:37 1993
Received: by amber.unm.edu (4.1/3.1)
	id <AA14731@amber.unm.edu>; Fri, 26 Nov 93 23:08:49 MST
Date: Fri, 26 Nov 93 23:08:49 MST
From: wester@amber.unm.edu (Michael Wester)
Message-Id: <9311270608.AA14731@amber.unm.edu>
To: damrau@amber.unm.edu (Jackie Damrau)
Subject: New address.tex
Status: R

%%% ===========================================================================
%%%  @LaTeX-style-file{
%%%    author            = "Michael Wester",
%%%    version           = "1.0",
%%%    date              = "November 26, 1993",
%%%    time              = "NULL",
%%%    filename          = "address.tex",
%%%    address           = "Department of Mathematics and Statistics
%%%                         University of New Mexico
%%%                         Albuquerque, New Mexico   87131",
%%%    telephone         = "NA",
%%%    FAX               = "NA",
%%%    checksum          = "NA",
%%%    email             = "wester@math.unm.edu (Internet)",
%%%    codetable         = "ISO/ASCII",
%%%
%%%    keywords          = "LaTeX, TeX, formletter, mailing labels",
%%%    supported         = "yes",
%%%
%%%    docstring         = "Generate form letters to a list of recipients,
%%%                         using either TeX or LaTeX.  The addresses of the
%%%                         recipients are kept in a single file, separated by
%%%                         blank lines.  The addresses can be of arbitrary
%%%                         length with the first line consisting of the
%%%                         recipient's name.  The letter template can
%%%                         reference the recipient's name by \Name and the
%%%                         lines of the address by \Address.  Individual lines
%%%                         within the address can be selected by \AddrLine{N}
%%%                         (N > 0).  If a preamble file exists, it will be
%%%                         \input before any processing of addresses takes
%%%                         place (and in LaTeX, before the \begin{document}).
%%%                         In addition, the counts \Naddr and \Laddr refer to
%%%                         the number of the current address being processed
%%%                         from the recipient file and the number of \\
%%%                         separated lines the current value of \Address
%%%                         contains, respectively.  Finally, unless
%%%                         \breakupfalse has been executed in the preamble,
%%%                         the \Name will be intelligently broken up into its
%%%                         components.  These components, some of which may be
%%%                         compound or blank, can be referenced by
%%%                         \SocialTitle, \FirstName, \MiddleName, \LastName,
%%%                         \Suffix and \OtherTitle.  This last operation is
%%%                         optional because it can be time consuming to
%%%                         perform and so should be done only if the
%%%                         capability is desired.",
%%%  }
%%% ===========================================================================
%
% Define default filenames.  The last three can be usefully redefined in the
% preamble.
%
\def\DEFAULTpreamble{preamble}
\def\DEFAULTtolist{tolist}
\def\DEFAULTletter{letter}
\def\DEFAULTpostamble{postamble}
%
\newcount\Laddr    % number of lines in the address
\newcount\Naddr    % number of the address in the tolist
\newif\ifbreakup   % decide whether a name should be broken up componentwise
\newif\ifnotdone   % conditional used to continue processing loops
\newif\ifwmember   % set by \wmember when testing for list membership
\newread\file      % temporary file
\newread\tolist    % file containing the list of recipients
\newtoks\CR        % used to separate lines within the address
\newtoks\CumAddr   % cumulative address
\newtoks\LineAddr  % one line of the address
%
\breakuptrue       % by default, break up a name into its components
\CR={\\}           % LaTeX newline macro
\def\blank{\par}   % make \ifx happy when checking for blank lines
% =============================================================================
%                                     MACROS
% =============================================================================
%
% Print a line of text followed by a newline.
%
\def\print#1{\immediate\write16{#1}}
%
% Test if a macro has been previously defined.
%
\def\ifundefined#1{\expandafter\ifx\csname#1\endcsname\relax}
%
% Allow \par's within \loop constructs.
%
\long\def\loop#1\repeat{\def\body{#1}\iterate}
% -----------------------------------------------------------------------------
%
% \addspace\A adds a space after \A unless \A is null.
%
\def\addspace#1{\ifx#1\empty
                   \empty
                \else
                   #1
                \fi}
%
% \topbox{H}{W}{text} creates a top aligned box of height H and width W
%                     containing the specified text.
%
\def\topbox#1#2#3{\leavevmode\vtop to #1{\hsize=#2 #3\vfil\eject}}
% -----------------------------------------------------------------------------
%
% \AddrLine{N} selects the Nth line of the \Address.  \Current is a macro
%              holding the last selection.
%
\def\AddrLine#1{\GetLine#1\of\Address}
%
% \AddrBlock{N} selects the Nth block of the \Address.  Blocks are one or more
%               lines of text separated by lines containing only --- and
%               whitespace.  \Current is a macro holding the last selection.
%
\def\AddrBlock#1{\GetBlock#1\of\Address\by{\\---\\}}
%
% \GetLine{N}\of\List gets line N of the \List.  The lines in the \List are
%                     assumed to be separated by \\'s.  \Current will hold the
%                     last line selected.
%
\def\GetLine#1\of#2{\GetBlock#1\of#2\by\\}
%
% \GetBlock{N}\of\List\by\Delim gets block N of the \List.  The blocks in the
%                               \List are assumed to be separated by \Delim's.
%                               \Current will hold the last block selected.
%
\def\Current{}
\def\GetBlock#1\of#2\by#3{{\count0=#1
                           \toks0=\expandafter{#2#3}%
                           \edef\List{\the\toks0}%
                           \def\lcar##1#3##2\nil{##1}%
                           \def\lcdr##1#3##2\nil{##2}%
                           \notdonetrue
                           \loop
                              \ifx\List\empty
                                 \notdonefalse
                                 \gdef\Current{}%
                              \else
                                 \advance\count0 by -1
                                 \ifnum\count0=0
                                    \notdonefalse
                                    \toks0=\expandafter\expandafter
                                              \expandafter{%
                                                 \expandafter\lcar\List\nil}%
                                    \xdef\Current{\the\toks0}%
                                    \Current
                                 \fi
                              \fi
                              \ifnotdone
                                 \toks0=\expandafter\expandafter
                                           \expandafter{%
                                              \expandafter\lcdr\List\nil}%
                                 \edef\List{\the\toks0}%
                           \repeat}}
%
% \StoreAddrBlock{N}\in\A stores the Nth block of \Address in the macro \A.
%
\def\StoreAddrBlock#1\in#2{{\setbox0=\hbox{\AddrBlock#1}%
                            \toks0=\expandafter{\Current}%
                            \xdef#2{\the\toks0}}}
% -----------------------------------------------------------------------------
%
% Break up a name into its components: \SocialTitle (e.g., Dr.)
%                                      \FirstName
%                                      \MiddleName (may be more than one word)
%                                      \LastName
%                                      \Suffix (e.g., Jr.)
%                                      \OtherTitle (e.g., Manager)
%
\def\andList{\& and}
\def\ParticleList{de della van von}
\def\SocialTitleList{Doctor Dr. Hon. Master Mister Miss Mr. Mrs. Ms. Prof.
                     Professor Rabbi Rev. The Sir}
\def\SuffixList{Jr. Junior Sr. II III IV}
\def\The{The}
\def\breakup#1\nil{{\gdef\SocialTitle{}%
                    \gdef\Suffix{}%
                    \gdef\OtherTitle{}%
                    %
                    \edef\List{#1}%
                    %
                    % Remove any phrase following a comma.  It should either be
                    % a suffix (like Jr.) or an academic or professional title
                    % (or perhaps both of these combined).
                    %
                    \setq\tmp{\commacdr\List, \nil}%
                    \ifnull\tmp
                    \else
                       \setq\Phrase{\commacdr\List\nil}%
                       \setq\List{\commacar\List\nil}%
                       %
                       % Check if the \Phrase itself contains a comma and if
                       % so, if the first component is a suffix.
                       %
                       \setq\tmp{\commacdr\Phrase, \nil}%
                       \ifx\tmp\empty
                          \edef\carPhrase{\Phrase}%
                       \else
                          \setq\carPhrase{\commacar\Phrase\nil}%
                          \gsetq\OtherTitle{\commacdr\Phrase\nil}%
                       \fi
                       \wmember\carPhrase\of\SuffixList\nil
                       \ifwmember
                          \xdef\Suffix{\carPhrase}%
                       \else
                          \xdef\OtherTitle{\Phrase}%
                       \fi
                    \fi
                    %
                    % Check if the first word is an honorific title.  This
                    % needs to be repeated to handle European constructs like
                    % Herr Doctor Professor.
                    %
                    \loop
                       \setq\carList{\wcar\List\nil}%
                       \wmember\carList\of\SocialTitleList\nil
                       \ifwmember
                          \ifx\SocialTitle\empty
                             \xdef\SocialTitle{\carList}%
                          \else
                             \gnconc\SocialTitle\carList
                          \fi
                          \setq\List{\wcdr\List\nil}%
                          \setq\carList{\wcar\List\nil}%
                          %
                          % If the title begins with "The", then grab the next
                          % word as well.
                          %
                          \ifx\SocialTitle\The
                             \gnconc\SocialTitle\carList
                             \setq\List{\wcdr\List\nil}%
                             \setq\carList{\wcar\List\nil}%
                          \fi
                          %
                          % If the next word is an "and", then this is a
                          % compound title so add the "and" and the following
                          % word into the title.
                          %
                          \wmember\carList\of\andList\nil
                          \ifwmember
                             \gnconc\SocialTitle\carList
                             \setq\List{\wcdr\List\nil}%
                             \setq\carList{\wcar\List\nil}%
                             \gnconc\SocialTitle\carList
                             \setq\List{\wcdr\List\nil}%
                          \fi
                    \repeat
                    %
                    % Check if the last word is a suffix (like III).
                    %
                    \wendcarcdr\List\nil\endcarList\tmp
                    \edef\List{\tmp}%
                    \wmember\endcarList\of\SuffixList\nil
                    \ifwmember
                       \xdef\Suffix{\endcarList}%
                       \wendcarcdr\List\nil\endcarList\tmp
                       \edef\List{\tmp}%
                    \fi
                    %
                    % Assume that the last word in what remains of the \List is
                    % the last name, and gobble it up.
                    %
                    \xdef\LastName{\endcarList}%
                    %
                    % If the word that preceded the last name is a particle,
                    % add it into the last name (this is usually correct,
                    % although not always: e.g., Ludwig van Beethoven).
                    %
                    \wendcarcdr\List\nil\endcarList\tmp
                    \wmember\endcarList\of\ParticleList\nil
                    \ifwmember
                       \gnendconc\endcarList\LastName
                       \edef\List{\tmp}%
                    \fi
                    %
                    % Assume that the first word in what remains of the \List
                    % is the first name, and gobble it up.
                    %
                    \gsetq\FirstName{\wcar\List\nil}%
                    \setq\List{\wcdr\List\nil}%
                    \setq\carList{\wcar\List\nil}%
                    %
                    % If the next word is an "and", then this is a compound
                    % first name so add the "and" and the following word into
                    % the first name.
                    %
                    \wmember\carList\of\andList\nil
                    \ifwmember
                       \gnconc\FirstName\carList
                       \setq\List{\wcdr\List\nil}%
                       \setq\carList{\wcar\List\nil}%
                       \gnconc\FirstName\carList
                       \setq\List{\wcdr\List\nil}%
                    \fi
                    %
                    % Any remaining words will be taken to be middle names.
                    %
                    \xdef\MiddleName{\List}}}
\def\commacar#1, #2\nil{#1}
\def\commacdr#1, #2\nil{#2}
% -----------------------------------------------------------------------------
%                                Lisplike MACROS
% -----------------------------------------------------------------------------
%
% \ABDReverseExpand{D}{C}{B}{A} first expands A, then expands B, then expands
%                               D.
%
\def\ABDReverseExpand#1#2#3#4{\expandafter\expandafter
                                 \expandafter#1%
                                    \expandafter\expandafter
                                       \expandafter#2\expandafter#3#4}
%
% Used to remove leading spaces.
%
\def\pretrim.#1{#1}
%
% Test for {}.
%
\def\ifnull#1{\ifx#1\empty}
%
% \setq\A{B} assigns the expansion of B to the macro \A.
% \gsetq\A{B} globally assigns the expansion of B to the macro \A.
%
\def\setq#1#2{\edef#1{\expandafter#2}}
\def\gsetq#1#2{\xdef#1{\expandafter#2}}
%
% \nconc\A\B appends the expansion of \B onto \A.
% \gnconc\A\B globally appends the expansion of \B onto \A.
% \gnendconc\A\B globally prepends the expansion of \A onto \B.
%
\def\nconc#1#2{\edef#1{#1\space#2}}
\def\gnconc#1#2{\xdef#1{#1\space#2}}
\def\gnendconc#1#2{\xdef#2{#1\space#2}}
%
% \wcar\List\nil picks off the first word (string of nonblank characters) in
%                the \List.  If the \List is blank or empty then a null string
%                is returned.
%
\def\wcar#1\nil{\ifnull#1
                   \empty
                \else
                   \expandafter\wCar\pretrim.#1 \nil
                \fi}
\def\wCar#1 #2\nil{#1}
%
% \wcdr\List\nil removes the first word from the \List and any preceding
%                blanks.  If the \List is blank or empty then a null string is
%                returned.
%
\def\wcdr#1\nil{\ifnull#1
                   \empty
                \else
                   \ABDReverseExpand\ifx\empty\wCdr\pretrim.#1 \nil
                      \empty
                   \else
                      \expandafter\wCdr\pretrim.#1\nil
                   \fi
                \fi}
\def\wCdr#1 #2\nil{#2}
%
% \wendcarcdr\List\nil\A\B picks off the last word in the \List and places it
%                          in \A.  The rest of the list (stripped of leading
%                          blanks) is placed in \B.
%
\def\wendcarcdr#1\nil#2#3{{\edef\List{#1}%
                           \def\carList{}%
                           \def\newList{}%
                           %
                           \notdonetrue
                           \loop
                              \ifnull\List
                                 \notdonefalse
                                 \xdef#2{\carList}%
                                 \xdef#3{\newList}%
                              \else
                                 \ifx\List\space
                                    \notdonefalse
                                    \xdef#2{}%
                                    \xdef#3{}%
                                 \fi
                              \fi
                              \ifnotdone
                                 \ifx\newList\empty
                                    \edef\newList{\carList}%
                                 \else
                                    \nconc\newList\carList
                                 \fi
                                 \setq\carList{\wcar\List\nil}%
                                 \setq\List{\wcdr\List\nil}%
                           \repeat}}
%
% \wmember\Element\of\List\nil causes \ifwmember to be true if the \Element is
%                              a member of the \List and false otherwise.
%
\def\wmember#1\of#2\nil{{\global\wmemberfalse
                         \edef\Element{#1}%
                         \edef\List{#2}%
                         \setq\carList{\wcar\List\nil}%
                         %
                         \ifnull\Element
                            \notdonefalse
                         \else
                            \notdonetrue
                         \fi
                         \loop
                            \ifnull\carList
                               \notdonefalse
                            \fi
                            \ifnotdone
                               \ifx\Element\carList
                                  \notdonefalse
                                  \global\wmembertrue
                               \else
                                  \setq\List{\wcdr\List\nil}%
                                  \setq\carList{\wcar\List\nil}%
                               \fi
                         \repeat}}
% =============================================================================
\print{}
%
% Check for a preamble and \input it if it exists.
%
\message{Enter the filename of the preamble [\DEFAULTpreamble.tex]: }
\read-1 to \filename
\ifx\filename\blank                        % is \filename blank?
   \def\filename{\DEFAULTpreamble\space}   % use default value
\fi
\openin\file=\filename
\ifeof\file   % do nothing if the file does not exist
\else
   \closein\file
   \input\filename
   \print{}
\fi
%
% Do the appropriate initialization of the document depending on whether this
% is TeX or LaTeX.  The \begin{document} in LaTeX needs to precede the opening
% of the tolist for styles other than letter (noted by Geoff Grinton).
%
\ifundefined{LaTeX}
\else
   \begin{document}
\fi
%
\message{Enter filename of recipients' addresses [\DEFAULTtolist.tex]: }
\read-1 to \filename
\ifx\filename\blank                      % is \filename blank?
   \def\filename{\DEFAULTtolist\space}   % use default value
\fi
\openin\tolist=\filename
\ifeof\tolist
   \message{\filename cannot be opened.}\print{}\end{document}
\fi
%
\message{Enter the filename of the letter template [\DEFAULTletter.tex]: }
\read-1 to \filename
\ifx\filename\blank                      % is \filename blank?
   \def\filename{\DEFAULTletter\space}   % use default value
\fi
\openin\file=\filename
\ifeof\file
   \message{\filename cannot be opened.}\print{}\end{document}
\fi
\closein\file
%
\print{}
%
% Force TeX not to append an end-of-line character to the lines below (and so
% prevent the insertion of unwanted spaces into the document).
%
\endlinechar=-1
%
\Naddr=0
\loop
   \ifeof\tolist
      \notdonefalse
   \else
      \notdonetrue
   \fi
   \ifnotdone
      \read\tolist to \Name
      %
      % Ignore excess blank lines and lines that are commented out.
      %
      \ifx\Name\empty
      \else
         \advance\Naddr by 1
         %
         % Break up the name into its component parts if desired.
         %
         \ifbreakup
            \expandafter\breakup\Name\nil
         \fi
         %
         % Read the lines of the address.
         %
         \Laddr=0
         \def\Address{}%
         \read\tolist to \Line
         {
         \loop
            %
            % If \Line is blank then have finished gobbling up the address.
            %
            \ifx\Line\empty
               \notdonefalse
            \else
               \notdonetrue
               \global\advance\Laddr by 1
               \LineAddr=\expandafter{\Line}%
               \ifx\Address\empty
                  \xdef\Address{\the\LineAddr}%
               \else
                  \CumAddr=\expandafter{\Address}%
                  \xdef\Address{\the\CumAddr\the\CR\the\LineAddr}%
               \fi
            \fi
            \ifnotdone
               \read\tolist to \Line
         \repeat
         }%
         %
         % Temporarily restore normal TeX conventions for end of lines while
         % reading in the template file.
         %
         \endlinechar=13
         \input\filename
         \endlinechar=-1
      \fi
\repeat
%
\closein\tolist
%
% Check for a postamble and \input it if it exists.
%
\def\filename{\DEFAULTpostamble\space}
\openin\file=\filename
\ifeof\file   % do nothing if the file does not exist
\else
   \closein\file
   \input\filename
\fi
%
% Final clean up
%
\ifundefined{LaTeX}
\else
   \clearpage
\fi
%
\end{document}