From wester@amber.unm.edu Sat Nov 27 00:08:37 1993 Received: by amber.unm.edu (4.1/3.1) id ; 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}