% 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