% This macro source file is from the four volume series
% "TeX in Practice" by Stephan von Bechtolsheim, published
% 1993 by Springer-Verlag, New York.
% Copyright 1993 Stephan von Bechtolsheim.
% No warranty or liability is assumed.
% This macro may be copied freely if no fees other than
% media cost or shipping charges are charged and as long
% as this copyright and the following source code itself
% is not changed. Please see the series for further information.
%
% Version: 1.0
% Date: May 1, 1993
%
%
% This source code is documented in 37.2.7.1, p. IV-170.
% Original source in file "o4.TEX", starting line 461.
\wlog{L: "out-ds.tip" ["o4.TEX," l. 461, p. IV-170]}%
% This file DOES NOT belong to format "texip."
\InputD{box-mac.tip}
\InputD{rangetst.tip}
\InputD{maxmindi.tip}
\InputD{prot.tip}
\InputD{vtbox.tip}
\InputD{nathd.tip}
\InputD{shiftudb.tip}
\catcode`\@ = 11
\newdimen\@PageWidth
\newdimen\@ColWidth
\newdimen\@ColIndent
\newdimen\@PageHeight
\newcount\@DSCCurNumberOfColumns
\newcount\@PageLayoutCodeDSC
\newcount\@DSCDebugging
\def\SetUpDSC #1#2#3#4#5#6{%
   \@DSCDebugging = #6
   \CheckZeroOneRange{\@DSCDebugging}%
       {\string\SetUpDSC: debugging code wrong}
   \ifnum\@DSCDebugging = 0
       \ProtWritefalse
   \else
       \ProtWritetrue
   \fi
   \InitProtWrite
   \WriteProtocol{0}{\string\SetUpDSC: begin}%
   \@PageWidth = #1%
   \@ColWidth = #2%
   \@ColIndent = #3%
   \@PageHeight = #4%
   \@PageLayoutCodeDSC = #5%
   \CheckZeroOneRange{\@PageLayoutCodeDSC}%
       {\string\SetUpDSC: page layout code wrong}%
   \hsize = \@PageWidth
   \dimen0 = 2\@ColIndent
   \advance\dimen0 by 2\@ColWidth
   \ifdim\dimen0 > \hsize
       \errmessage{\string\SetUpDSC: Initial values of
           \string\hsize, \noexpand\@ColWidth and
           \noexpand\@ColIndent do not make sense.}%
   \fi
   \vsize = \@PageHeight
   \@DSCCurNumberOfColumns = 1
   \@SetSingleColumnOutput{As part of \string\SetUpDSC}%
   \ifcase \@PageLayoutCodeDSC
       \topskip = 10pt             % Top / bottom flush
   \or
       \topskip = 10pt plus 50pt   % Ragged bottom
   \else
       \errmessage{\string\SetUpDSC: illegal
           page layout parameter.}%
   \fi
   \WriteProtocol{0}{\string\SetUpDSC: end}%
}
\def\UnDoDSC{%
   \global\output = {\plainoutput}%
}
\newbox\@BoxOfPageSoFar
\newbox\@DSCLeftColumnBox
\newdimen\@DSCLeftNaturalHeight
\newbox\@DSCRightColumnBox
\newdimen\@DSCRightNaturalHeight
\newbox\@DSCTempBox
\def\@EliminateRulesConditional{%
   \ifnum\@DSCDebugging = 0
       \EliminateRuledBoxes
   \fi
}
\newif\if@CaseCPageBreak
\def\@EjectPenalty{-10000 }
\def\eject{\penalty\@EjectPenalty}
\def\@BalancePenalty{-10001 }
\def\@PageLine{\hbox to \@PageWidth}
\def\@PageLineR{\HboxR to \@PageWidth}
\def\@ProtDSC #1{
   \WriteProtocol{1}{\string\@ProtDSC: Begin (page \the\pageno)}
   \WriteProtocol{2}{"#1",
       number of cols: \the\@DSCCurNumberOfColumns}
   \WriteProtocol{2}{\string\vsize: \the\vsize,
       \string\pagetotal: \the\pagetotal,
       \string\pagegoal: \the\pagegoal}
   \BoxToProtocol{2}{\@BoxOfPageSoFar}{}
   \WriteProtocol{2}{\string\dimen0: \the\dimen0,
       \string\outputpenalty: \the\outputpenalty}
   \WriteProtocol{1}{\string\@ProtDSC: End}
}
\def\@CaseReport #1{%
   \WriteProtocol{0}{CASE #1}%
   \message{CASE #1.}%
}
\def\@SetOutputRoutine #1#2#3{%
   #1\output = {#2}%
   \WriteProtocol{0}{\string\@SetOutputRoutine:}%
   \WriteProtocol{1}{#3}%
}
\def\@SingleColumnOutputRoutine{%
   \@EliminateRulesConditional
   \WriteProtocol{0}{\noexpand\@SingleColumnOutputRoutine called,
       \noexpand\outputpenalty is \the\outputpenalty}
   \WriteProtocol{1}{Page number is \the\pageno}
   \showboxdepth   = 2
   \showboxbreadth = 1000
   \@ProtDSC{\string\@SingleColumnOutputRoutine: begin}%
   \BoxToProtocol{1}{255}{}%
   \@CheckNumberOfColumns{1}{\@SingleColumnOutputRoutine}%
   \@ShipAPageBox{%
       \ifnum\@PageLayoutCodeDSC = 0
           \vbox to \@PageHeight{\unvbox 255}%
       \else
           \vbox to \@PageHeight{\unvbox 255 \vfill}%
       \fi
   }%
}
\def\@SetSingleColumnOutput #1{%
   \WriteProtocol{0}{\string\@SetSingleColumnOutput: set to single
               column output!}%
   \global\@DSCCurNumberOfColumns = 1
   \@SetOutputRoutine{\global}{\@SingleColumnOutputRoutine}{#1}%
   \global\vsize    = \@PageHeight
   \global\pagegoal = \@PageHeight
}
\def\@SaveCurrentPageOutputRoutine{%
   \global\setbox\@BoxOfPageSoFar = \vbox{\unvbox 255}%
   \BoxToProtocol{0}{\@BoxOfPageSoFar}%
       {\string\@SaveCurrentPageOutputRoutine}
}
\newcount\@VsizeFactor
\def\@ComputeVsizeForDoubleColumns{%
   \vsize = \@PageHeight
   \advance\vsize by -\ht\@BoxOfPageSoFar
   \advance\vsize by -\dp\@BoxOfPageSoFar
   \multiply\vsize by 2
   \@VsizeFactor = \vsize
   \divide\@VsizeFactor by \baselineskip
   \ifodd\@VsizeFactor
       \advance\@VsizeFactor by -1
   \fi
   \global\vsize = \@VsizeFactor \baselineskip
   \@ProtDSC{\string\@ComputeVsizeForDoubleColumns}%
}
\newdimen\@HalfVsize
\def\@ComputeHalfVsize{%
   \@HalfVsize = \vsize
   \divide\@HalfVsize by 2
}
\def\BeginDoubleColumns{%
   \par
   \WriteProtocol{0}{\string\BeginDoubleColumns: begin}
   \@CheckNumberOfColumns{1}{\BeginDoubleOfColumns}%
   \global\@DSCCurNumberOfColumns = 2
   \begingroup
   \@CaseCPageBreakfalse
   \@EliminateRulesConditional
   \WriteProtocol{1}{\string\BeginDoubleColumns: put rest of page
                           into \string\@BoxOfPageSoFar}%
   \@SetOutputRoutine{}{\@SaveCurrentPageOutputRoutine}%
       {\string\BeginDoubleColumns: save page built up to now.}%
   \eject
   \@ProtDSC{\string\BeginDoubleColumns: 2}%
   \@SetOutputRoutine{}{\@DoubleColumnOutputRoutine}%
       {Double column output set up by \string\BeginDoubleColumns}%
   \hsize = \@ColWidth
   \@ComputeVsizeForDoubleColumns
   \@ProtDSC{\string\BeginDoubleColumns: 3}%
}
\def\EndDoubleColumns{%
   \par
   \WriteProtocol{0}{\string\EndDoubleColumns: begin}
   \@CheckNumberOfColumns{2}{\EndDoubleOfColumns}
   \@ProtDSC{In \string\EndDoubleColumns}
   \@CaseReport{A}
   \penalty\@BalancePenalty
   \@BuildPageSoFar
   \@SetSingleColumnOutput{\string\EndDoubleColumns}
   \endgroup
}
\def\@DoubleColumnOutputRoutine{%
   \@EliminateRulesConditional
   \WriteProtocol{0}{\string\@DoubleColumnOutputRoutineput:
                   begin (penalty: \the\outputpenalty)}
   \if@CaseCPageBreak
       \@CaseReport{D/E}%
       \ifvoid\@DSCLeftColumnBox
           \errmessage{\string\@DoubleColumnOutputRoutine:
               missing left column!}%
       \else
           \ifvoid\@DSCRightColumnBox
               \global\setbox\@DSCRightColumnBox =
                   \vbox{\unvbox 255}%
               \global\@DSCRightNaturalHeight =
                   \ht\@DSCRightColumnBox
               \ifnum\outputpenalty = \@EjectPenalty
                   \errmessage{\string\@DoubleColumnOutput:
                       \noexpand\eject in right column illegal.}%
                   \@CaseReport{D}
                   \MaxDimen{\dimen0}{\@DSCRightNaturalHeight}%
                                     {\@DSCLeftNaturalHeight}{}%
               \else
                   \ifnum\outputpenalty = \@BalancePenalty
                       \@CaseReport{E}
                       \MaxDimen{\dimen0}{\@DSCRightNaturalHeight}
                                         {\@DSCLeftNaturalHeight}{}%
                   \else
                       \@CaseReport{D}%
                       \dimen0 = \vsize
                   \fi
               \fi
               \@SetColumnBox{\@DSCLeftColumnBox}{}%
               \@SetColumnBox{\@DSCRightColumnBox}{}%
               \Vtbox{\@DSCRightColumnBox}{\global}%
               \ShiftRefPointUpOrDown{\@DSCRightColumnBox}{12pt}%
               \Vtbox{\@DSCLeftColumnBox}{\global}%
               \ShiftRefPointUpOrDown{\@DSCLeftColumnBox}{12pt}%
               \ifnum\outputpenalty > -10000
                   \@ShipAPageBox{%
                       \ifnum\@PageLayoutCodeDSC = 0
                           \vbox to \@PageHeight{\@BuildPageSoFar}%
                       \else
                           \vbox to \@PageHeight{\@BuildPageSoFar
                                                       \vfill}%
                       \fi
                   }%
               \else
                   % It's \EndDoubleColumns!
               \fi
               \global\@CaseCPageBreakfalse
               \@ComputeVsizeForDoubleColumns
               \global\pagegoal = \vsize
           \else
               \errmessage{\string\@DoubleColumnOutputRoutine:
                       left / right columns messed up.}
           \fi
       \fi
   \else
       \ifnum\outputpenalty = \@EjectPenalty
           \@CaseReport{C}
           \WriteProtocol{1}{\string\@DoubleColumnOutputRoutineput:
                       penalty \@EjectPenalty call.}
           \global\@CaseCPageBreaktrue
           \ifvoid\@DSCLeftColumnBox
               \global\setbox\@DSCLeftColumnBox =
                   \vbox{\unvbox 255}
               \global\@DSCLeftNaturalHeight = \ht\@DSCLeftColumnBox
               \@ComputeHalfVsize
               \WriteProtocol{2}{*\string\vsize/2 is
                                       \the\@HalfVsize}%
               \dimen1 = \ht\@DSCLeftColumnBox
               \advance\dimen1 by \dp\@DSCLeftColumnBox
               \ifdim\dimen1 > \@HalfVsize
                   \message{WARNING: column is to long!}%
               \fi
               \global\pagegoal = \@HalfVsize
               \global\vsize    = \@HalfVsize
           \else
               \errmessage{\string\@DoubleColumnOutputRoutine:
                   left column box already loaded!}%
           \fi
       \else
           \@StandardBalanceColumns
           \ifnum\outputpenalty = \@BalancePenalty
               \@CaseReport{A}%
           \else
               \@CaseReport{B}%
               \@ShipAPageBox{%
                   \ifnum\@PageLayoutCodeDSC = 0
                       \vbox to \@PageHeight{\@BuildPageSoFar}%
                   \else
                       \vbox to \@PageHeight{\@BuildPageSoFar
                                                       \vfill}%
                   \fi
               }%
               \@ComputeVsizeForDoubleColumns
               \global\pagegoal = \vsize
           \fi
       \fi
   \fi
}
\newcount\@EmptyBoxesBuildPageCount
\def\@BuildPageSoFar{%
   \@EmptyBoxesBuildPageCount = 0
   \ifvoid\@BoxOfPageSoFar
       \advance\@EmptyBoxesBuildPageCount by 1
   \fi
   \ifvoid\@DSCLeftColumnBox
       \advance\@EmptyBoxesBuildPageCount by 1
   \fi
   \ifvoid\@DSCRightColumnBox
       \advance\@EmptyBoxesBuildPageCount by 1
   \fi
   \WriteProtocol{1}{\string\@BuildPageSoFar: begin
           (\noexpand\@EmptyBoxesBuildPageCount is
           \the\@EmptyBoxesBuildPageCount)}
   \ifnum\@EmptyBoxesBuildPageCount < 3
       \BoxToProtocol{2}{\@BoxOfPageSoFar}{}
       \BoxToProtocol{2}{\@DSCLeftColumnBox}{}
       \BoxToProtocol{2}{\@DSCRightColumnBox}{}
       \unvbox\@BoxOfPageSoFar         % May be empty.
       \wd\@DSCLeftColumnBox = \@ColWidth      % Left column.
       \wd\@DSCRightColumnBox = \@ColWidth     % Right column.
       \@PageLine{%
           \hskip\@ColIndent
           \BoxR\@DSCLeftColumnBox
           \hfil
           \BoxR\@DSCRightColumnBox
           \hskip\@ColIndent
       }
       \smallskip
   \fi
   \WriteProtocol{1}{\string\@BuildPageSoFar: end}
}
\def\@SetColumnBox #1#2{%
   \global\setbox#1 = \vbox to \dimen0{\unvbox#1 #2}%
   \Vtbox{#1}{\global}%
   \ShiftRefPointUpOrDown{#1}{12pt}%
}
\def\@StandardBalanceColumns{%
   \setbox\@DSCTempBox = \vbox{\unvcopy 255}
   \dimen0 = \ht\@DSCTempBox
   \advance\dimen0 by \dp\@DSCTempBox
   \advance\dimen0 by \topskip
   \divide\dimen0 by 2
   \WriteProtocol{1}{\string\@StandardBalanceColumns:
       \noexpand\dimen0 is \the\dimen0, page \the\pageno.}
   \@BalanceColumns{\dimen0}%
}
\def\@BalanceColumns #1{%
   \@ProtDSC{\string\@BalanceColumns: Start}%
   \@EliminateRulesConditional
   \ifvoid\@DSCLeftColumnBox\else
       \errmessage{\string\@BalanceColumns: left column box
           not empty.}%
   \fi
   \ifvoid\@DSCRightColumnBox\else
       \errmessage{\string\@BalanceColumns: right column box
           not empty.}%
   \fi
   \ifvoid 255
       \errmessage{\string\@BalanceColumns: box 255 is void.}%
   \fi
   \setbox\@DSCTempBox = \vbox{\unvbox 255}
   \dimen0 = #1
   \splittopskip = \topskip
   \BoxToProtocol{0}{\@DSCTempBox}{Before \noexpand\vsplit loop}%
   {%
       \vbadness = 10000   % Don't report underfull boxes.
       \loop
           \global\setbox\@DSCRightColumnBox = \copy\@DSCTempBox
           \global\setbox\@DSCLeftColumnBox =
                   \vsplit\@DSCRightColumnBox to \dimen0
           \WriteProtocol{1}{\string\dimen0: \the\dimen0}%
           \BoxToProtocol{1}{\@DSCLeftColumnBox}%
               {[1] (left column) in \noexpand\vsplit loop}%
           \BoxToProtocol{1}{\@DSCRightColumnBox}%
               {[2] (right column) in \noexpand\vsplit loop}%
           \NaturalHeight{\dimen3}{\@DSCLeftColumnBox}%
           \NaturalHeight{\dimen4}{\@DSCRightColumnBox}%
           \WriteProtocol{3}{\string\dimen3: \the\dimen3}%
           \WriteProtocol{3}{\string\dimen4: \the\dimen4}%
           \advance\dimen3 by 1sp
           \ifdim\dimen4 > \dimen3
               \global\advance\dimen0 by 1pt
       \repeat
   }
   \setbox\@DSCLeftColumnBox  = \vbox{\unvbox\@DSCLeftColumnBox}%
   \setbox\@DSCRightColumnBox  = \vbox{\unvbox\@DSCRightColumnBox}%
   \MaxDimen{\dimen0}{\ht\@DSCLeftColumnBox}%
                   {\ht\@DSCRightColumnBox}{}%
   \ifcase\@PageLayoutCodeDSC
       \@SetColumnBox{\@DSCLeftColumnBox}{}%
       \@SetColumnBox{\@DSCRightColumnBox}{}%
   \or
       \@SetColumnBox{\@DSCLeftColumnBox}{\vfill}%
       \@SetColumnBox{\@DSCRightColumnBox}{\vfill}%
   \fi
   \WriteProtocol{1}{\string\@BalanceColumns:
       balancing done.}%
}
\def\DSCHeader{%
   \@PageLineR{%
       \bf Header
       \hfil
       \tt \the\pageno
   }%
}
\def\DSCFooter{%
%   \@PageLineR{%
%       \vrule width \@PageWidth height 1pt depth 2pt
%   }%
   \@PageLineR{%
       \bf FOOTER
       \hfil
       \tt \the\pageno
   }%
}
\def\@ShipAPageBox #1{%
   \WriteProtocol{0}{\string\@ShipAPageBox:
       called (page \the\pageno)}%
   \shipout\vbox{%
       \@EliminateRulesConditional
       \DSCHeader
       \vskip 12pt
       #1
       \vskip 12pt
       \DSCFooter
   }
   \WriteProtocol{0}%
       {\string\@ShipAPageBox: done (page \the\pageno)}%
   \advancepageno
}
\def\@CheckNumberOfColumns #1#2{%
   \ifnum \@DSCCurNumberOfColumns = #1\relax
   \else
       \errmessage{\string\@CheckNumberOfColumns: [\string#2]:
           currently \the\@DSCCurNumberOfColumns\space columns,
           should be #1.}
   \fi
}
\def\bye{}
\def\bye{%
   \@CheckNumberOfColumns{1}{\string\bye: Still in double
       column mode, forgotten a \string\EndDoubleColumns?}%
   \vfill\supereject
   \end
}
\catcode`\@ = 12